diff --git a/.image_optim.yml b/.image_optim.yml index 4a9ad0555c..746b85dc3b 100644 --- a/.image_optim.yml +++ b/.image_optim.yml @@ -2,10 +2,11 @@ skip_missing_workers: true allow_lossy: false # PNG advpng: false -optipng: +optipng: level: 2 pngcrush: false pngout: false pngquant: false # JPG jpegrecompress: false +timeout: 15 diff --git a/.travis.yml b/.travis.yml index 465c6366df..b8b2dad4f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,10 @@ env: - DISCOURSE_HOSTNAME=www.example.com - RUBY_GC_MALLOC_LIMIT=50000000 matrix: - - "RAILS_MASTER=0" - - "RAILS_MASTER=1" + - "RAILS_MASTER=0 QUNIT_RUN=0" + - "RAILS_MASTER=1 QUNIT_RUN=0" + - "RAILS_MASTER=0 QUNIT_RUN=1" + - "RAILS_MASTER=1 QUNIT_RUN=1" addons: postgresql: 9.5 @@ -19,11 +21,12 @@ addons: matrix: allow_failures: - - env: "RAILS_MASTER=1" - - rvm: rbx-2 + - env: "RAILS_MASTER=1 QUNIT_RUN=0" + - env: "RAILS_MASTER=1 QUNIT_RUN=1" fast_finish: true rvm: + - 2.4.1 - 2.3.3 services: @@ -57,4 +60,4 @@ install: - bash -c "if [ '$RAILS_MASTER' == '1' ]; then bundle update --retry=3 --jobs=3 arel rails seed-fu; fi" - bash -c "if [ '$RAILS_MASTER' == '0' ]; then bundle install --without development --deployment --retry=3 --jobs=3; fi" -script: "bundle exec rspec && bundle exec rake plugin:spec && bundle exec rake qunit:test['200000']" +script: bash -c "if [ '$QUNIT_RUN' == '0' ]; then bundle exec rspec && bundle exec rake plugin:spec; else bundle exec rake qunit:test['200000']; fi" diff --git a/Gemfile b/Gemfile index 3f8f8f153b..8cb3ff20b5 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,9 @@ source 'https://rubygems.org' # if there is a super emergency and rubygems is playing up, try #source 'http://production.cf.rubygems.org' +# does not install in linux ATM, so hack this for now +# gem 'bootsnap', require: false + def rails_master? ENV["RAILS_MASTER"] == '1' end @@ -66,10 +69,9 @@ gem 'unf', require: false gem 'email_reply_trimmer', '0.1.6' -# note: for image_optim to correctly work you need to follow -# https://github.com/toy/image_optim -# pinned due to https://github.com/toy/image_optim/pull/75, docker image must be upgraded to upgrade -gem 'image_optim', '0.20.2' +# TODO Use official image_optim gem once https://github.com/toy/image_optim/pull/149 +# is merged. +gem 'discourse_image_optim', require: 'image_optim' gem 'multi_json' gem 'mustache' gem 'nokogiri' @@ -96,10 +98,7 @@ gem 'thor', require: false gem 'rest-client' gem 'rinku' gem 'sanitize' -gem 'sass' -gem 'sass-rails' gem 'sidekiq' -gem 'sidekiq-statistic' # for sidekiq web gem 'sinatra', require: false @@ -117,6 +116,7 @@ group :assets do end group :test do + gem 'webmock', require: false gem 'fakeweb', '~> 1.3.0', require: false gem 'minitest', require: false gem 'timecop' @@ -127,7 +127,7 @@ end group :test, :development do gem 'rspec' gem 'mock_redis' - gem 'listen', '0.7.3', require: false + gem 'listen', require: false gem 'certified', require: false # later appears to break Fabricate(:topic, category: category) gem 'fabrication', '2.9.8', require: false @@ -184,3 +184,12 @@ gem 'memory_profiler', require: false, platform: :mri gem 'rmmseg-cpp', require: false gem 'logster' + +gem 'sassc', require: false + + +if ENV["IMPORT"] == "1" + gem 'mysql2' + gem 'php_serialize' + gem 'redcarpet' +end diff --git a/Gemfile.lock b/Gemfile.lock index 2572555d91..a8cdb56386 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,47 +1,48 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.2.7.1) - actionpack (= 4.2.7.1) - actionview (= 4.2.7.1) - activejob (= 4.2.7.1) + actionmailer (4.2.8) + actionpack (= 4.2.8) + actionview (= 4.2.8) + activejob (= 4.2.8) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.7.1) - actionview (= 4.2.7.1) - activesupport (= 4.2.7.1) + actionpack (4.2.8) + actionview (= 4.2.8) + activesupport (= 4.2.8) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.7.1) - activesupport (= 4.2.7.1) + actionview (4.2.8) + activesupport (= 4.2.8) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.2) + rails-html-sanitizer (~> 1.0, >= 1.0.3) active_model_serializers (0.8.3) activemodel (>= 3.0) - activejob (4.2.7.1) - activesupport (= 4.2.7.1) + activejob (4.2.8) + activesupport (= 4.2.8) globalid (>= 0.3.0) - activemodel (4.2.7.1) - activesupport (= 4.2.7.1) + activemodel (4.2.8) + activesupport (= 4.2.8) builder (~> 3.1) - activerecord (4.2.7.1) - activemodel (= 4.2.7.1) - activesupport (= 4.2.7.1) + activerecord (4.2.8) + activemodel (= 4.2.8) + activesupport (= 4.2.8) arel (~> 6.0) - activesupport (4.2.7.1) + activesupport (4.2.8) i18n (~> 0.7) - json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) annotate (2.7.1) activerecord (>= 3.2, < 6.0) rake (>= 10.4, < 12.0) - arel (6.0.3) + arel (6.0.4) aws-sdk (2.5.3) aws-sdk-resources (= 2.5.3) aws-sdk-core (2.5.3) @@ -61,21 +62,29 @@ GEM rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - builder (3.2.2) + builder (3.2.3) bullet (5.4.2) activesupport (>= 3.0.0) uniform_notifier (~> 1.10.0) byebug (9.0.6) certified (1.0.0) coderay (1.1.1) - concurrent-ruby (1.0.2) + concurrent-ruby (1.0.5) connection_pool (2.2.0) + crack (0.4.3) + safe_yaml (~> 1.0.0) crass (1.0.2) debug_inspector (0.0.2) - diff-lcs (1.2.5) + diff-lcs (1.3) discourse-qunit-rails (0.0.9) railties discourse_fastimage (2.0.3) + discourse_image_optim (0.24.4) + exifr (~> 1.2, >= 1.2.2) + fspath (~> 3.0) + image_size (~> 1.5) + in_threads (~> 1.3) + progress (~> 3.0, >= 3.0.1) domain_name (0.5.25) unf (>= 0.0.5, < 1.0.0) email_reply_trimmer (0.1.6) @@ -95,7 +104,7 @@ GEM erubis (2.7.0) excon (0.53.0) execjs (2.7.0) - exifr (1.2.4) + exifr (1.2.5) fabrication (2.9.8) fakeweb (1.3.0) faraday (0.11.0) @@ -105,15 +114,16 @@ GEM rake rake-compiler fast_xs (0.8.0) - ffi (1.9.17) + ffi (1.9.18) flamegraph (0.9.5) foreman (0.82.0) thor (~> 0.19.1) - fspath (2.1.1) + fspath (3.1.0) gc_tracer (1.5.1) globalid (0.3.7) activesupport (>= 4.1.0) guess_html_encoding (0.0.11) + hashdiff (0.3.2) hashie (3.5.5) highline (1.7.8) hiredis (0.6.1) @@ -121,25 +131,21 @@ GEM http-cookie (1.0.2) domain_name (~> 0.5) http_accept_language (2.0.5) - i18n (0.7.0) - image_optim (0.20.2) - exifr (~> 1.1, >= 1.1.3) - fspath (~> 2.1) - image_size (~> 1.3) - in_threads (~> 1.3) - progress (~> 3.0, >= 3.0.1) - image_size (1.4.1) - in_threads (1.3.1) + i18n (0.8.1) + image_size (1.5.0) + in_threads (1.4.0) jmespath (1.3.1) jquery-rails (4.2.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (1.8.6) jwt (1.5.6) - kgio (2.10.0) + kgio (2.11.0) libv8 (5.3.332.38.5) - listen (0.7.3) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) logster (1.2.7) loofah (2.0.3) nokogiri (>= 1.5.9) @@ -151,22 +157,22 @@ GEM rack (>= 1.1.3) metaclass (0.0.4) method_source (0.8.2) - mime-types (2.99.2) + mime-types (2.99.3) mini_portile2 (2.1.0) mini_racer (0.1.9) libv8 (~> 5.3) - minitest (5.9.1) + minitest (5.10.1) mocha (1.1.0) metaclass (~> 0.0.1) mock_redis (0.15.4) - moneta (0.8.1) - msgpack (1.0.2) + moneta (1.0.0) + msgpack (1.1.0) multi_json (1.12.1) multi_xml (0.6.0) multipart-post (2.0.0) - mustache (1.0.3) + mustache (1.0.5) netrc (0.11.0) - nokogiri (1.6.8.1) + nokogiri (1.7.1) mini_portile2 (~> 2.1.0) nokogumbo (1.4.10) nokogiri @@ -206,19 +212,19 @@ GEM omniauth-twitter (1.3.0) omniauth-oauth (~> 1.1) rack - onebox (1.8.3) + onebox (1.8.4) fast_blank (>= 1.0.0) - htmlentities (~> 4.3.4) - moneta (~> 0.8) + htmlentities (~> 4.3) + moneta (~> 1.0) multi_json (~> 1.11) mustache - nokogiri (~> 1.6.6) + nokogiri (~> 1.7) sanitize openid-redis-store (0.0.2) redis ruby-openid pg (0.19.0) - progress (3.1.1) + progress (3.3.1) pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -227,6 +233,7 @@ GEM pry (>= 0.9.10, < 0.11.0) pry-rails (0.3.4) pry (>= 0.9.10) + public_suffix (2.0.5) puma (3.6.0) r2 (0.2.6) rack (1.6.5) @@ -239,34 +246,34 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.2.7.1) - actionmailer (= 4.2.7.1) - actionpack (= 4.2.7.1) - actionview (= 4.2.7.1) - activejob (= 4.2.7.1) - activemodel (= 4.2.7.1) - activerecord (= 4.2.7.1) - activesupport (= 4.2.7.1) + rails (4.2.8) + actionmailer (= 4.2.8) + actionpack (= 4.2.8) + actionview (= 4.2.8) + activejob (= 4.2.8) + activemodel (= 4.2.8) + activerecord (= 4.2.8) + activesupport (= 4.2.8) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.7.1) + railties (= 4.2.8) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.7) + rails-dom-testing (1.0.8) activesupport (>= 4.2.0.beta, < 5.0) - nokogiri (~> 1.6.0) + nokogiri (~> 1.6) rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) rails_multisite (1.0.6) rails (> 4.2, < 5) - railties (4.2.7.1) - actionpack (= 4.2.7.1) - activesupport (= 4.2.7.1) + railties (4.2.8) + actionpack (= 4.2.8) + activesupport (= 4.2.8) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - raindrops (0.17.0) - rake (11.2.2) + raindrops (0.18.0) + rake (11.3.0) rake-compiler (0.9.9) rake rb-fsevent (0.9.7) @@ -314,17 +321,17 @@ GEM ruby-readability (0.7.0) guess_html_encoding (>= 0.0.4) nokogiri (>= 1.6.0) + ruby_dep (1.5.0) + safe_yaml (1.0.4) sanitize (4.4.0) crass (~> 1.0.2) nokogiri (>= 1.4.4) nokogumbo (~> 1.4.1) - sass (3.2.19) - sass-rails (5.0.4) - railties (>= 4.0.0, < 5.0) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) + sass (3.4.23) + sassc (1.11.2) + bundler + ffi (~> 1.9.6) + sass (>= 3.3.0) seed-fu (2.3.5) activerecord (>= 3.1, < 4.3) activesupport (>= 3.1, < 4.3) @@ -339,8 +346,6 @@ GEM connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) redis (~> 3.2, >= 3.2.1) - sidekiq-statistic (1.2.0) - sidekiq (>= 3.3.4, < 5) simple-rss (1.3.1) sinatra (1.4.6) rack (~> 1.4) @@ -351,32 +356,36 @@ GEM spork-rails (4.0.0) rails (>= 3.0.0, < 5) spork (>= 1.0rc0) - sprockets (3.6.3) + sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.1.1) + sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) stackprof (0.2.10) test_after_commit (1.1.0) activerecord (>= 3.2) - thor (0.19.1) - thread_safe (0.3.5) + thor (0.19.4) + thread_safe (0.3.6) tilt (2.0.5) timecop (0.8.1) trollop (2.1.2) - tzinfo (1.2.2) + tzinfo (1.2.3) thread_safe (~> 0.1) uglifier (3.0.2) execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext unf_ext (0.0.7.1) - unicorn (5.2.0) + unicorn (5.3.0) kgio (~> 2.6) raindrops (~> 0.7) uniform_notifier (1.10.0) + webmock (3.0.1) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff PLATFORMS ruby @@ -394,6 +403,7 @@ DEPENDENCIES certified discourse-qunit-rails discourse_fastimage (= 2.0.3) + discourse_image_optim email_reply_trimmer (= 0.1.6) ember-handlebars-template (= 0.7.5) ember-rails (= 0.18.5) @@ -412,8 +422,7 @@ DEPENDENCIES hiredis htmlentities http_accept_language (~> 2.0.5) - image_optim (= 0.20.2) - listen (= 0.7.3) + listen logster lru_redux mail @@ -462,12 +471,10 @@ DEPENDENCIES rtlit ruby-readability sanitize - sass - sass-rails + sassc seed-fu (~> 2.3.5) shoulda sidekiq - sidekiq-statistic simple-rss sinatra spork-rails @@ -478,6 +485,7 @@ DEPENDENCIES uglifier unf unicorn + webmock BUNDLED WITH 1.14.6 diff --git a/README.md b/README.md index 359c8189f1..9a4c6f7595 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,24 @@ ![Logo](images/discourse.png) -Discourse is the 100% open source discussion platform built for the next decade of the Internet. It works as: +Discourse is the 100% open source discussion platform built for the next decade of the Internet. Use it as a: -- a mailing list -- a discussion forum -- a long-form chat room +- mailing list +- discussion forum +- long-form chat room To learn more about the philosophy and goals of the project, [visit **discourse.org**](http://www.discourse.org). ## Screenshots - - - - -Atom   -Soylent +Boing Boing + + + -Browse [lots more notable Discourse instances](http://www.discourse.org/faq/customers/). +Mobile + +Browse [lots more notable Discourse instances](https://www.discourse.org/customers). ## Development @@ -38,12 +38,12 @@ If you're looking for business class hosting, see [discourse.org/buy](https://ww Discourse is built for the *next* 10 years of the Internet, so our requirements are high: -| Browsers | Tablets | Smartphones | +| Browsers | Tablets | Phones | | -------- | ------- | ----------- | -| Safari 6.1+| iPad 2+ | iOS 7+ | -| Google Chrome 23+ | Android 4.3+ | Android 4.3+ | -| Internet Explorer 11+ | Windows 8 | Windows Phone 8 | -| Firefox 16+ | | +| Safari 6.1+ | iPad 3+ | iOS 8+ | +| Google Chrome 32+ | Android 4.3+ | Android 4.3+ | +| Internet Explorer 11+ | | | +| Firefox 27+ | | | ## Built With diff --git a/app/assets/fonts/FontAwesome.otf b/app/assets/fonts/FontAwesome.otf index 3ed7f8b48a..401ec0f36e 100644 Binary files a/app/assets/fonts/FontAwesome.otf and b/app/assets/fonts/FontAwesome.otf differ diff --git a/app/assets/fonts/fontawesome-webfont.eot b/app/assets/fonts/fontawesome-webfont.eot index 9b6afaedc0..e9f60ca953 100644 Binary files a/app/assets/fonts/fontawesome-webfont.eot and b/app/assets/fonts/fontawesome-webfont.eot differ diff --git a/app/assets/fonts/fontawesome-webfont.svg b/app/assets/fonts/fontawesome-webfont.svg index d05688e9e2..855c845e53 100644 --- a/app/assets/fonts/fontawesome-webfont.svg +++ b/app/assets/fonts/fontawesome-webfont.svg @@ -1,655 +1,2671 @@ - - + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/fonts/fontawesome-webfont.ttf b/app/assets/fonts/fontawesome-webfont.ttf index 26dea7951a..35acda2fa1 100644 Binary files a/app/assets/fonts/fontawesome-webfont.ttf and b/app/assets/fonts/fontawesome-webfont.ttf differ diff --git a/app/assets/fonts/fontawesome-webfont.woff b/app/assets/fonts/fontawesome-webfont.woff index dc35ce3c2c..400014a4b0 100644 Binary files a/app/assets/fonts/fontawesome-webfont.woff and b/app/assets/fonts/fontawesome-webfont.woff differ diff --git a/app/assets/fonts/fontawesome-webfont.woff2 b/app/assets/fonts/fontawesome-webfont.woff2 index 500e517253..4d13fc6040 100644 Binary files a/app/assets/fonts/fontawesome-webfont.woff2 and b/app/assets/fonts/fontawesome-webfont.woff2 differ diff --git a/app/assets/javascripts/admin/adapters/theme.js.es6 b/app/assets/javascripts/admin/adapters/theme.js.es6 new file mode 100644 index 0000000000..df9c8830d1 --- /dev/null +++ b/app/assets/javascripts/admin/adapters/theme.js.es6 @@ -0,0 +1,20 @@ +import RestAdapter from 'discourse/adapters/rest'; + +export default RestAdapter.extend({ + basePath() { + return "/admin/"; + }, + + afterFindAll(results) { + let map = {}; + results.forEach(theme => {map[theme.id] = theme;}); + results.forEach(theme => { + let mapped = theme.get("child_themes") || []; + mapped = mapped.map(t => map[t.id]); + theme.set("childThemes", mapped); + }); + return results; + }, + + jsonMode: true +}); diff --git a/app/assets/javascripts/admin/components/ace-editor.js.es6 b/app/assets/javascripts/admin/components/ace-editor.js.es6 index a03865c40c..67d084e21e 100644 --- a/app/assets/javascripts/admin/components/ace-editor.js.es6 +++ b/app/assets/javascripts/admin/components/ace-editor.js.es6 @@ -7,6 +7,13 @@ export default Ember.Component.extend({ _editor: null, _skipContentChangeEvent: null, + @observes('editorId') + editorIdChanged() { + if (this.get('autofocus')) { + this.send('focus'); + } + }, + @observes('content') contentChanged() { if (this._editor && !this._skipContentChangeEvent) { @@ -14,6 +21,13 @@ export default Ember.Component.extend({ } }, + @observes('mode') + modeChanged() { + if (this._editor && !this._skipContentChangeEvent) { + this._editor.getSession().setMode("ace/mode/" + this.get('mode')); + } + }, + _destroyEditor: function() { if (this._editor) { this._editor.destroy(); @@ -41,6 +55,7 @@ export default Ember.Component.extend({ editor.setTheme("ace/theme/chrome"); editor.setShowPrintMargin(false); + editor.setOptions({fontSize: "14px"}); editor.getSession().setMode("ace/mode/" + this.get('mode')); editor.on('change', () => { this._skipContentChangeEvent = true; @@ -48,6 +63,7 @@ export default Ember.Component.extend({ this._skipContentChangeEvent = false; }); editor.$blockScrolling = Infinity; + editor.renderer.setScrollMargin(10,10); this.$().data('editor', editor); this._editor = editor; @@ -55,7 +71,20 @@ export default Ember.Component.extend({ // xxx: don't run during qunit tests this.appEvents.on('ace:resize', self, self.resize); } + + if (this.get("autofocus")) { + this.send("focus"); + } }); }); + }, + + actions: { + focus() { + if (this._editor) { + this._editor.focus(); + this._editor.navigateFileEnd(); + } + } } }); diff --git a/app/assets/javascripts/admin/components/color-input.js.es6 b/app/assets/javascripts/admin/components/color-input.js.es6 index 98d5f6e6bb..005c4f5d4b 100644 --- a/app/assets/javascripts/admin/components/color-input.js.es6 +++ b/app/assets/javascripts/admin/components/color-input.js.es6 @@ -1,3 +1,5 @@ +import {default as loadScript, loadCSS } from 'discourse/lib/load-script'; + /** An input field for a color. @@ -6,19 +8,36 @@ @params valid is a boolean indicating if the input field is a valid color. **/ export default Ember.Component.extend({ + classNames: ['color-picker'], hexValueChanged: function() { var hex = this.get('hexValue'); + let $text = this.$('input.hex-input'); + if (this.get('valid')) { - this.$('input').attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';'); + $text.attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';'); + + if (this.get('pickerLoaded')) { + this.$('.picker').spectrum({color: "#" + this.get('hexValue')}); + } } else { - this.$('input').attr('style', ''); + $text.attr('style', ''); } }.observes('hexValue', 'brightnessValue', 'valid'), - _triggerHexChanged: function() { - var self = this; - Em.run.schedule('afterRender', function() { - self.hexValueChanged(); + didInsertElement() { + loadScript('/javascripts/spectrum.js').then(()=>{ + loadCSS('/javascripts/spectrum.css').then(()=>{ + Em.run.schedule('afterRender', ()=>{ + this.$('.picker').spectrum({color: "#" + this.get('hexValue')}) + .on("change.spectrum", (me, color)=>{ + this.set('hexValue', color.toHexString().replace("#","")); + }); + this.set('pickerLoaded', true); + }); + }); }); - }.on('didInsertElement') + Em.run.schedule('afterRender', ()=>{ + this.hexValueChanged(); + }); + } }); diff --git a/app/assets/javascripts/admin/components/customize-link.js.es6 b/app/assets/javascripts/admin/components/customize-link.js.es6 deleted file mode 100644 index 0600f6b5cd..0000000000 --- a/app/assets/javascripts/admin/components/customize-link.js.es6 +++ /dev/null @@ -1,12 +0,0 @@ -import { getOwner } from 'discourse-common/lib/get-owner'; - -export default Ember.Component.extend({ - router: function() { - return getOwner(this).lookup('router:main'); - }.property(), - - active: function() { - const id = this.get('customization.id'); - return this.get('router.url').indexOf(`/customize/css_html/${id}/css`) !== -1; - }.property('router.url', 'customization.id') -}); diff --git a/app/assets/javascripts/admin/components/inline-edit-checkbox.js.es6 b/app/assets/javascripts/admin/components/inline-edit-checkbox.js.es6 new file mode 100644 index 0000000000..5c168760c7 --- /dev/null +++ b/app/assets/javascripts/admin/components/inline-edit-checkbox.js.es6 @@ -0,0 +1,36 @@ +import {default as computed, observes} from "ember-addons/ember-computed-decorators"; + +export default Ember.Component.extend({ + init(){ + this._super(); + this.set("checkedInternal", this.get("checked")); + }, + + classNames: ['inline-edit'], + + @observes("checked") + checkedChanged() { + this.set("checkedInternal", this.get("checked")); + }, + + @computed("labelKey") + label(key) { + return I18n.t(key); + }, + + @computed("checked", "checkedInternal") + changed(checked, checkedInternal) { + return (!!checked) !== (!!checkedInternal); + }, + + actions: { + cancelled(){ + this.set("checkedInternal", this.get("checked")); + }, + + finished(){ + this.set("checked", this.get("checkedInternal")); + this.sendAction(); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-colors-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-colors-show.js.es6 new file mode 100644 index 0000000000..e333d2a58d --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-customize-colors-show.js.es6 @@ -0,0 +1,74 @@ +import computed from 'ember-addons/ember-computed-decorators'; + +export default Ember.Controller.extend({ + @computed("model.colors","onlyOverridden") + colors(allColors, onlyOverridden) { + if (onlyOverridden) { + return allColors.filter(color => color.get("overridden")); + } else { + return allColors; + } + }, + + actions: { + + revert: function(color) { + color.revert(); + }, + + undo: function(color) { + color.undo(); + }, + + copyToClipboard() { + $(".table.colors").hide(); + let area = $(""); + $(".table.colors").after(area); + area.text(this.get("model").schemeJson()); + let range = document.createRange(); + range.selectNode(area[0]); + window.getSelection().addRange(range); + let successful = document.execCommand('copy'); + if (successful) { + this.set("model.savingStatus", I18n.t("admin.customize.copied_to_clipboard")); + } else { + this.set("model.savingStatus", I18n.t("admin.customize.copy_to_clipboard_error")); + } + + setTimeout(()=>{ + this.set("model.savingStatus", null); + }, 2000); + + window.getSelection().removeAllRanges(); + + $(".table.colors").show(); + $(area).remove(); + }, + + copy() { + var newColorScheme = Em.copy(this.get('model'), true); + newColorScheme.set('name', I18n.t('admin.customize.colors.copy_name_prefix') + ' ' + this.get('model.name')); + newColorScheme.save().then(()=>{ + this.get('allColors').pushObject(newColorScheme); + this.replaceRoute('adminCustomize.colors.show', newColorScheme); + }); + }, + + save: function() { + this.get('model').save(); + }, + + destroy: function() { + + const model = this.get('model'); + return bootbox.confirm(I18n.t("admin.customize.colors.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => { + if (result) { + model.destroy().then(()=>{ + this.get('allColors').removeObject(model); + this.replaceRoute('adminCustomize.colors'); + }); + } + }); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 index ae253aec84..87166e386f 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 @@ -1,10 +1,14 @@ -export default Ember.Controller.extend({ - onlyOverridden: false, +import showModal from 'discourse/lib/show-modal'; +export default Ember.Controller.extend({ baseColorScheme: function() { return this.get('model').findBy('is_base', true); }.property('model.@each.id'), + baseColorSchemes: function() { + return this.get('model').filterBy('is_base', true); + }.property('model.@each.id'), + baseColors: function() { var baseColorsHash = Em.Object.create({}); _.each(this.get('baseColorScheme.colors'), function(color){ @@ -13,99 +17,25 @@ export default Ember.Controller.extend({ return baseColorsHash; }.property('baseColorScheme'), - removeSelected() { - this.get('model').removeObject(this.get('selectedItem')); - this.set('selectedItem', null); - }, - - filterContent: function() { - if (!this.get('selectedItem')) { return; } - - if (!this.get('onlyOverridden')) { - this.set('colors', this.get('selectedItem.colors')); - return; - } - - const matches = []; - _.each(this.get('selectedItem.colors'), function(color){ - if (color.get('overridden')) matches.pushObject(color); - }); - - this.set('colors', matches); - }.observes('onlyOverridden'), - - updateEnabled: function() { - var selectedItem = this.get('selectedItem'); - if (selectedItem.get('enabled')) { - this.get('model').forEach(function(c) { - if (c !== selectedItem) { - c.set('enabled', false); - c.startTrackingChanges(); - c.notifyPropertyChange('description'); - } - }); - } - }, - actions: { - selectColorScheme: function(colorScheme) { - if (this.get('selectedItem')) { this.get('selectedItem').set('selected', false); } - this.set('selectedItem', colorScheme); - this.set('colors', colorScheme.get('colors')); - colorScheme.set('savingStatus', null); - colorScheme.set('selected', true); - this.filterContent(); + + newColorSchemeWithBase(baseKey) { + const base = this.get('baseColorSchemes').findBy('base_scheme_id', baseKey); + const newColorScheme = Em.copy(base, true); + newColorScheme.set('name', I18n.t('admin.customize.colors.new_name')); + newColorScheme.set('base_scheme_id', base.get('base_scheme_id')); + newColorScheme.save().then(()=>{ + this.get('model').pushObject(newColorScheme); + newColorScheme.set('savingStatus', null); + this.replaceRoute('adminCustomize.colors.show', newColorScheme); + }); }, newColorScheme() { - const newColorScheme = Em.copy(this.get('baseColorScheme'), true); - newColorScheme.set('name', I18n.t('admin.customize.colors.new_name')); - this.get('model').pushObject(newColorScheme); - this.send('selectColorScheme', newColorScheme); - this.set('onlyOverridden', false); + showModal('admin-color-scheme-select-base', { model: this.get('baseColorSchemes'), admin: true}); }, - revert: function(color) { - color.revert(); - }, - undo: function(color) { - color.undo(); - }, - - toggleEnabled: function() { - var selectedItem = this.get('selectedItem'); - selectedItem.toggleProperty('enabled'); - selectedItem.save({enabledOnly: true}); - this.updateEnabled(); - }, - - save: function() { - this.get('selectedItem').save(); - this.updateEnabled(); - }, - - copy(colorScheme) { - var newColorScheme = Em.copy(colorScheme, true); - newColorScheme.set('name', I18n.t('admin.customize.colors.copy_name_prefix') + ' ' + colorScheme.get('name')); - this.get('model').pushObject(newColorScheme); - this.send('selectColorScheme', newColorScheme); - }, - - destroy: function() { - var self = this, - item = self.get('selectedItem'); - - return bootbox.confirm(I18n.t("admin.customize.colors.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) { - if (result) { - if (item.get('newRecord')) { - self.removeSelected(); - } else { - item.destroy().then(function(){ self.removeSelected(); }); - } - } - }); - } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-css-html-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-css-html-show.js.es6 deleted file mode 100644 index 47cf280ae6..0000000000 --- a/app/assets/javascripts/admin/controllers/admin-customize-css-html-show.js.es6 +++ /dev/null @@ -1,78 +0,0 @@ -import { url } from 'discourse/lib/computed'; - -const sections = ['css', 'header', 'top', 'footer', 'head-tag', 'body-tag', - 'mobile-css', 'mobile-header', 'mobile-top', 'mobile-footer', - 'embedded-css']; - -const activeSections = {}; -sections.forEach(function(s) { - activeSections[Ember.String.camelize(s) + "Active"] = Ember.computed.equal('section', s); -}); - - -export default Ember.Controller.extend(activeSections, { - maximized: false, - section: null, - - previewUrl: url("model.key", "/?preview-style=%@"), - downloadUrl: url('model.id', '/admin/site_customizations/%@'), - - mobile: function() { - return this.get('section').indexOf('mobile-') === 0; - }.property('section'), - - maximizeIcon: function() { - return this.get('maximized') ? 'compress' : 'expand'; - }.property('maximized'), - - saveButtonText: function() { - return this.get('model.isSaving') ? I18n.t('saving') : I18n.t('admin.customize.save'); - }.property('model.isSaving'), - - saveDisabled: function() { - return !this.get('model.changed') || this.get('model.isSaving'); - }.property('model.changed', 'model.isSaving'), - - adminCustomizeCssHtml: Ember.inject.controller(), - - undoPreviewUrl: url('/?preview-style='), - defaultStyleUrl: url('/?preview-style=default'), - - actions: { - save() { - this.get('model').saveChanges(); - }, - - destroy() { - return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => { - if (result) { - const model = this.get('model'); - model.destroyRecord().then(() => { - this.get('adminCustomizeCssHtml').get('model').removeObject(model); - this.transitionToRoute('adminCustomizeCssHtml'); - }); - } - }); - }, - - toggleMaximize: function() { - this.toggleProperty('maximized'); - }, - - toggleMobile: function() { - const section = this.get('section'); - - // Try to send to the same tab as before - let dest; - if (this.get('mobile')) { - dest = section.replace('mobile-', ''); - if (sections.indexOf(dest) === -1) { dest = 'css'; } - } else { - dest = 'mobile-' + section; - if (sections.indexOf(dest) === -1) { dest = 'mobile-css'; } - } - this.replaceRoute('adminCustomizeCssHtml.show', this.get('model.id'), dest); - } - } - -}); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 new file mode 100644 index 0000000000..6ef86fc31b --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 @@ -0,0 +1,156 @@ +import { url } from 'discourse/lib/computed'; +import { default as computed, observes } from 'ember-addons/ember-computed-decorators'; + +export default Ember.Controller.extend({ + maximized: false, + section: null, + + targets: [ + {id: 0, name: I18n.t('admin.customize.theme.common')}, + {id: 1, name: I18n.t('admin.customize.theme.desktop')}, + {id: 2, name: I18n.t('admin.customize.theme.mobile')} + ], + + @computed('onlyOverridden') + showCommon() { + return this.shouldShow('common'); + }, + + @computed('onlyOverridden') + showDesktop() { + return this.shouldShow('desktop'); + }, + + @computed('onlyOverridden') + showMobile() { + return this.shouldShow('mobile'); + }, + + @observes('onlyOverridden') + onlyOverriddenChanged() { + if (this.get('onlyOverridden')) { + if (!this.get('model').hasEdited(this.get('currentTargetName'), this.get('fieldName'))) { + let target = (this.get('showCommon') && 'common') || + (this.get('showDesktop') && 'desktop') || + (this.get('showMobile') && 'mobile'); + + let fields = this.get('model.theme_fields'); + let field = fields && fields.find(f => (f.target === target)); + this.replaceRoute('adminCustomizeThemes.edit', this.get('model.id'), target, field && field.name); + } + } + }, + + shouldShow(target){ + if(!this.get("onlyOverridden")) { + return true; + } + return this.get("model").hasEdited(target); + }, + + currentTarget: 0, + + setTargetName: function(name) { + let target; + switch(name) { + case "common": target = 0; break; + case "desktop": target = 1; break; + case "mobile": target = 2; break; + } + + this.set("currentTarget", target); + }, + + @computed("currentTarget") + currentTargetName(target) { + switch(parseInt(target)) { + case 0: return "common"; + case 1: return "desktop"; + case 2: return "mobile"; + } + }, + + @computed("fieldName") + activeSectionMode(fieldName) { + return fieldName && fieldName.indexOf("scss") > -1 ? "scss" : "html"; + }, + + @computed("currentTargetName", "fieldName", "saving") + error(target, fieldName) { + return this.get('model').getError(target, fieldName); + }, + + @computed("fieldName", "currentTargetName") + editorId(fieldName, currentTarget) { + return fieldName + "|" + currentTarget; + }, + + @computed("fieldName", "currentTargetName", "model") + activeSection: { + get(fieldName, target, model) { + return model.getField(target, fieldName); + }, + set(value, fieldName, target, model) { + model.setField(target, fieldName, value); + return value; + } + }, + + @computed("currentTarget", "onlyOverridden") + fields(target, onlyOverridden) { + let fields = [ + "scss", "head_tag", "header", "after_header", "body_tag", "footer" + ]; + + if (parseInt(target) === 0) { + fields.push("embedded_scss"); + } + + if (onlyOverridden) { + const model = this.get("model"); + const targetName = this.get("currentTargetName"); + fields = fields.filter(name => model.hasEdited(targetName, name)); + } + + return fields.map(name=>{ + let hash = { + key: (`admin.customize.theme.${name}.text`), + name: name + }; + + if (name.indexOf("_tag") > 0) { + hash.icon = "file-text-o"; + } + + hash.title = I18n.t(`admin.customize.theme.${name}.title`); + + return hash; + }); + }, + + previewUrl: url('model.id', '/admin/themes/%@/preview'), + + maximizeIcon: function() { + return this.get('maximized') ? 'compress' : 'expand'; + }.property('maximized'), + + saveButtonText: function() { + return this.get('model.isSaving') ? I18n.t('saving') : I18n.t('admin.customize.save'); + }.property('model.isSaving'), + + saveDisabled: function() { + return !this.get('model.changed') || this.get('model.isSaving'); + }.property('model.changed', 'model.isSaving'), + + actions: { + save() { + this.set('saving', true); + this.get('model').saveChanges("theme_fields").finally(()=>{this.set('saving', false);}); + }, + + toggleMaximize: function() { + this.toggleProperty('maximized'); + } + } + +}); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 new file mode 100644 index 0000000000..3b2c2d9683 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 @@ -0,0 +1,175 @@ +import { default as computed } from 'ember-addons/ember-computed-decorators'; +import { url } from 'discourse/lib/computed'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; + +export default Ember.Controller.extend({ + + @computed("model", "allThemes") + parentThemes(model, allThemes) { + let parents = allThemes.filter(theme => + _.contains(theme.get("childThemes"), model)); + return parents.length === 0 ? null : parents; + }, + + @computed("model.theme_fields.@each") + hasEditedFields(fields) { + return fields.any(f=>!Em.isBlank(f.value)); + }, + + @computed('model.theme_fields.@each') + editedDescriptions(fields) { + let descriptions = []; + let description = target => { + let current = fields.filter(field => field.target === target && !Em.isBlank(field.value)); + if (current.length > 0) { + let text = I18n.t('admin.customize.theme.'+target); + let localized = current.map(f=>I18n.t('admin.customize.theme.'+f.name + '.text')); + return text + ": " + localized.join(" , "); + } + }; + ['common','desktop','mobile'].forEach(target=> { + descriptions.push(description(target)); + }); + return descriptions.reject(d=>Em.isBlank(d)); + }, + + previewUrl: url('model.id', '/admin/themes/%@/preview'), + + @computed("colorSchemeId", "model.color_scheme_id") + colorSchemeChanged(colorSchemeId, existingId) { + colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId); + return colorSchemeId !== existingId; + }, + + @computed("availableChildThemes", "model.childThemes.@each", "model", "allowChildThemes") + selectableChildThemes(available, childThemes, model, allowChildThemes) { + if (!allowChildThemes && (!childThemes || childThemes.length === 0)) { + return null; + } + + let themes = []; + available.forEach(t=> { + if (!childThemes || (childThemes.indexOf(t) === -1)) { + themes.push(t); + }; + }); + return themes.length === 0 ? null : themes; + }, + + @computed("allThemes", "allThemes.length", "model") + availableChildThemes(allThemes, count) { + if (count === 1) { + return null; + } + + let excludeIds = [this.get("model.id")]; + + let themes = []; + allThemes.forEach(theme => { + if (excludeIds.indexOf(theme.get("id")) === -1) { + themes.push(theme); + } + }); + + return themes; + }, + + downloadUrl: url('model.id', '/admin/themes/%@'), + + actions: { + + updateToLatest() { + this.set("updatingRemote", true); + this.get("model").updateToLatest() + .catch(popupAjaxError) + .finally(()=>{ + this.set("updatingRemote", false); + }); + }, + + checkForThemeUpdates() { + this.set("updatingRemote", true); + this.get("model").checkForUpdates() + .catch(popupAjaxError) + .finally(()=>{ + this.set("updatingRemote", false); + }); + }, + + cancelChangeScheme() { + this.set("colorSchemeId", this.get("model.color_scheme_id")); + }, + changeScheme(){ + let schemeId = this.get("colorSchemeId"); + this.set("model.color_scheme_id", schemeId === null ? null : parseInt(schemeId)); + this.get("model").saveChanges("color_scheme_id"); + }, + startEditingName() { + this.set("oldName", this.get("model.name")); + this.set("editingName", true); + }, + cancelEditingName() { + this.set("model.name", this.get("oldName")); + this.set("editingName", false); + }, + finishedEditingName() { + this.get("model").saveChanges("name"); + this.set("editingName", false); + }, + + editTheme() { + let edit = ()=>this.transitionToRoute('adminCustomizeThemes.edit', this.get('model.id'), 'common', 'scss'); + + if (this.get("model.remote_theme")) { + bootbox.confirm(I18n.t("admin.customize.theme.edit_confirm"), result => { + if (result) { + edit(); + } + }); + } else { + edit(); + } + }, + + applyDefault() { + const model = this.get("model"); + model.saveChanges("default").then(()=>{ + if (model.get("default")) { + this.get("allThemes").forEach(theme=>{ + if (theme !== model && theme.get('default')) { + theme.set("default", false); + } + }); + } + }); + }, + + applyUserSelectable() { + this.get("model").saveChanges("user_selectable"); + }, + + addChildTheme() { + let themeId = parseInt(this.get("selectedChildThemeId")); + let theme = this.get("allThemes").findBy("id", themeId); + this.get("model").addChildTheme(theme); + }, + + removeChildTheme(theme) { + this.get("model").removeChildTheme(theme); + }, + + destroy() { + return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => { + if (result) { + const model = this.get('model'); + model.destroyRecord().then(() => { + this.get('allThemes').removeObject(model); + this.transitionToRoute('adminCustomizeThemes'); + }); + } + }); + }, + + } + +}); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes.js.es6 new file mode 100644 index 0000000000..b9a897a26a --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes.js.es6 @@ -0,0 +1,10 @@ +import { default as computed } from 'ember-addons/ember-computed-decorators'; + +export default Ember.Controller.extend({ + @computed('model', 'model.@each') + sortedThemes(themes) { + return _.sortBy(themes.content, t => { + return [!t.get("default"), !t.get("user_selectable"), t.get("name").toLowerCase()]; + }); + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-group.js.es6 b/app/assets/javascripts/admin/controllers/admin-group.js.es6 index 6c3da11d7f..a0f4961b62 100644 --- a/app/assets/javascripts/admin/controllers/admin-group.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-group.js.es6 @@ -22,9 +22,9 @@ export default Ember.Controller.extend({ ]; }.property(), - @computed('model.visible', 'model.public', 'model.alias_level') + @computed('model.visible', 'model.public') disableMembershipRequestSetting(visible, publicGroup) { - return !visible || publicGroup || !this.get('model.canEveryoneMention'); + return !visible || publicGroup; }, @computed('model.visible', 'model.allow_membership_requests') diff --git a/app/assets/javascripts/admin/controllers/admin-groups-bulk-complete.js.es6 b/app/assets/javascripts/admin/controllers/admin-groups-bulk-complete.js.es6 new file mode 100644 index 0000000000..780543b724 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-groups-bulk-complete.js.es6 @@ -0,0 +1,4 @@ +export default Ember.Controller.extend({ + adminGroupsBulk: Ember.inject.controller(), + bulkAddResponse: Ember.computed.alias('adminGroupsBulk.bulkAddResponse') +}); diff --git a/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 b/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 index bf60519c5b..8f8b28f6b4 100644 --- a/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-groups-bulk.js.es6 @@ -6,6 +6,7 @@ export default Ember.Controller.extend({ users: null, groupId: null, saving: false, + bulkAddResponse: null, @computed('saving', 'users', 'groupId') buttonDisabled(saving, users, groupId) { @@ -24,7 +25,8 @@ export default Ember.Controller.extend({ ajax('/admin/groups/bulk', { data: { users, group_id: this.get('groupId') }, method: 'PUT' - }).then(() => { + }).then(result => { + this.set('bulkAddResponse', result); this.transitionToRoute('adminGroups.bulkComplete'); }).catch(popupAjaxError).finally(() => { this.set('saving', false); diff --git a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 index 1d4dac7e64..83fde41c7e 100644 --- a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 @@ -4,7 +4,7 @@ import Report from 'admin/models/report'; import computed from 'ember-addons/ember-computed-decorators'; export default Ember.Controller.extend({ - queryParams: ["mode", "start-date", "end-date", "category-id", "group-id"], + queryParams: ["mode", "start_date", "end_date", "category_id", "group_id"], viewMode: 'graph', viewingTable: Em.computed.equal('viewMode', 'table'), viewingGraph: Em.computed.equal('viewMode', 'graph'), @@ -28,7 +28,15 @@ export default Ember.Controller.extend({ @computed('model.type') showCategoryOptions(modelType) { - return !modelType.match(/_private_messages$/) && !modelType.match(/^page_view_/); + return [ + 'topics', + 'posts', + 'time_to_first_response_total', + 'topics_with_no_response', + 'flags', + 'likes', + 'bookmarks' + ].includes(modelType); }, @computed('model.type') @@ -42,13 +50,13 @@ export default Ember.Controller.extend({ this.set("refreshing", true); this.setProperties({ - 'start-date': this.get('startDate'), - 'end-date': this.get('endDate'), - 'category-id': this.get('categoryId'), + 'start_date': this.get('startDate'), + 'end_date': this.get('endDate'), + 'category_id': this.get('categoryId'), }); if (this.get('groupId')){ - this.set('group-id', this.get('groupId')); + this.set('group_id', this.get('groupId')); } q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId"), this.get("groupId")); diff --git a/app/assets/javascripts/admin/controllers/modals/admin-color-scheme-select-base.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-color-scheme-select-base.js.es6 new file mode 100644 index 0000000000..94939fa09f --- /dev/null +++ b/app/assets/javascripts/admin/controllers/modals/admin-color-scheme-select-base.js.es6 @@ -0,0 +1,14 @@ +import ModalFunctionality from 'discourse/mixins/modal-functionality'; + +export default Ember.Controller.extend(ModalFunctionality, { + + adminCustomizeColors: Ember.inject.controller(), + + actions: { + selectBase() { + this.get('adminCustomizeColors') + .send('newColorSchemeWithBase', this.get('selectedBaseThemeId')); + this.send('closeModal'); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/modals/admin-import-theme.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-import-theme.js.es6 new file mode 100644 index 0000000000..d59d419ef5 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/modals/admin-import-theme.js.es6 @@ -0,0 +1,35 @@ +import ModalFunctionality from 'discourse/mixins/modal-functionality'; +import { ajax } from 'discourse/lib/ajax'; +// import computed from 'ember-addons/ember-computed-decorators'; + +export default Ember.Controller.extend(ModalFunctionality, { + local: Ember.computed.equal('selection', 'local'), + remote: Ember.computed.equal('selection', 'remote'), + selection: 'local', + adminCustomizeThemes: Ember.inject.controller(), + + actions: { + importTheme() { + + let options = { + type: 'POST' + }; + + if (this.get('local')) { + options.processData = false; + options.contentType = false; + options.data = new FormData(); + options.data.append('theme', $('#file-input')[0].files[0]); + } else { + options.data = {remote: this.get('uploadUrl')}; + } + + ajax('/admin/themes/import', options).then(result=>{ + const theme = this.store.createRecord('theme',result.theme); + this.get('adminCustomizeThemes').send('addTheme', theme); + this.send('closeModal'); + }); + + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/modals/admin-theme-change.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-theme-change.js.es6 new file mode 100644 index 0000000000..82aba506a2 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/modals/admin-theme-change.js.es6 @@ -0,0 +1,13 @@ +import ModalFunctionality from 'discourse/mixins/modal-functionality'; +import { ajax } from 'discourse/lib/ajax'; + +export default Ember.Controller.extend(ModalFunctionality, { + loadDiff() { + this.set('loading', true); + ajax('/admin/logs/staff_action_logs/' + this.get('model.id') + '/diff') + .then(diff=>{ + this.set('loading', false); + this.set('diff', diff.side_by_side); + }); + } +}); diff --git a/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6 b/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6 deleted file mode 100644 index ca6ac31db1..0000000000 --- a/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6 +++ /dev/null @@ -1,20 +0,0 @@ -import ModalFunctionality from 'discourse/mixins/modal-functionality'; - -export default Ember.Controller.extend(ModalFunctionality, { - previousSelected: Ember.computed.equal('selectedTab', 'previous'), - newSelected: Ember.computed.equal('selectedTab', 'new'), - - onShow: function() { - this.send("selectNew"); - }, - - actions: { - selectNew: function() { - this.set('selectedTab', 'new'); - }, - - selectPrevious: function() { - this.set('selectedTab', 'previous'); - } - } -}); diff --git a/app/assets/javascripts/admin/controllers/modals/delete-site-customization-details.js.es6 b/app/assets/javascripts/admin/controllers/modals/delete-site-customization-details.js.es6 deleted file mode 100644 index 95537e305a..0000000000 --- a/app/assets/javascripts/admin/controllers/modals/delete-site-customization-details.js.es6 +++ /dev/null @@ -1,7 +0,0 @@ -import ChangeSiteCustomizationDetailsController from "admin/controllers/modals/change-site-customization-details"; - -export default ChangeSiteCustomizationDetailsController.extend({ - onShow() { - this.send("selectPrevious"); - } -}); diff --git a/app/assets/javascripts/admin/models/color-scheme.js.es6 b/app/assets/javascripts/admin/models/color-scheme.js.es6 index 743c779d6c..51e687f5c5 100644 --- a/app/assets/javascripts/admin/models/color-scheme.js.es6 +++ b/app/assets/javascripts/admin/models/color-scheme.js.es6 @@ -9,18 +9,26 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, { }, description: function() { - return "" + this.name + (this.enabled ? ' (*)' : ''); + return "" + this.name; }.property(), startTrackingChanges: function() { this.set('originals', { - name: this.get('name'), - enabled: this.get('enabled') + name: this.get('name') }); }, + schemeJson(){ + let buffer = []; + _.each(this.get('colors'), (c) => { + buffer.push(` "${c.get('name')}": "${c.get('hex')}"`); + }); + + return [`"${this.get("name")}": {`, buffer.join(",\n"), "}"].join("\n"); + }, + copy: function() { - var newScheme = ColorScheme.create({name: this.get('name'), enabled: false, can_edit: true, colors: Em.A()}); + var newScheme = ColorScheme.create({name: this.get('name'), can_edit: true, colors: Em.A()}); _.each(this.get('colors'), function(c){ newScheme.colors.pushObject(ColorSchemeColor.create({name: c.get('name'), hex: c.get('hex'), default_hex: c.get('default_hex')})); }); @@ -29,19 +37,16 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, { changed: function() { if (!this.originals) return false; - if (this.originals['name'] !== this.get('name') || this.originals['enabled'] !== this.get('enabled')) return true; + if (this.originals['name'] !== this.get('name')) return true; if (_.any(this.get('colors'), function(c){ return c.get('changed'); })) return true; return false; - }.property('name', 'enabled', 'colors.@each.changed', 'saving'), + }.property('name', 'colors.@each.changed', 'saving'), disableSave: function() { + if (this.get('theme_id')) { return false; } return !this.get('changed') || this.get('saving') || _.any(this.get('colors'), function(c) { return !c.get('valid'); }); }.property('changed'), - disableEnable: function() { - return !this.get('id') || this.get('saving'); - }.property('id', 'saving'), - newRecord: function() { return (!this.get('id')); }.property('id'), @@ -53,11 +58,11 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, { this.set('savingStatus', I18n.t('saving')); this.set('saving',true); - var data = { enabled: this.enabled }; + var data = {}; if (!opts || !opts.enabledOnly) { data.name = this.name; - + data.base_scheme_id = this.get('base_scheme_id'); data.colors = []; _.each(this.get('colors'), function(c) { if (!self.id || c.get('changed')) { @@ -78,8 +83,6 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, { _.each(self.get('colors'), function(c) { c.startTrackingChanges(); }); - } else { - self.set('originals.enabled', data.enabled); } self.set('savingStatus', I18n.t('saved')); self.set('saving', false); @@ -96,30 +99,25 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, { }); var ColorSchemes = Ember.ArrayProxy.extend({ - selectedItemChanged: function() { - var selected = this.get('selectedItem'); - _.each(this.get('content'),function(i) { - return i.set('selected', selected === i); - }); - }.observes('selectedItem') }); ColorScheme.reopenClass({ findAll: function() { var colorSchemes = ColorSchemes.create({ content: [], loading: true }); - ajax('/admin/color_schemes').then(function(all) { + return ajax('/admin/color_schemes').then(function(all) { _.each(all, function(colorScheme){ colorSchemes.pushObject(ColorScheme.create({ id: colorScheme.id, name: colorScheme.name, - enabled: colorScheme.enabled, is_base: colorScheme.is_base, + theme_id: colorScheme.theme_id, + theme_name: colorScheme.theme_name, + base_scheme_id: colorScheme.base_scheme_id, colors: colorScheme.colors.map(function(c) { return ColorSchemeColor.create({name: c.name, hex: c.hex, default_hex: c.default_hex}); }) })); }); - colorSchemes.set('loading', false); + return colorSchemes; }); - return colorSchemes; } }); diff --git a/app/assets/javascripts/admin/models/site-customization.js.es6 b/app/assets/javascripts/admin/models/site-customization.js.es6 deleted file mode 100644 index fe2176bf11..0000000000 --- a/app/assets/javascripts/admin/models/site-customization.js.es6 +++ /dev/null @@ -1,31 +0,0 @@ -import RestModel from 'discourse/models/rest'; - -const trackedProperties = [ - 'enabled', 'name', 'stylesheet', 'header', 'top', 'footer', 'mobile_stylesheet', - 'mobile_header', 'mobile_top', 'mobile_footer', 'head_tag', 'body_tag', 'embedded_css' -]; - -function changed() { - const originals = this.get('originals'); - if (!originals) { return false; } - return _.some(trackedProperties, (p) => originals[p] !== this.get(p)); -} - -const SiteCustomization = RestModel.extend({ - description: function() { - return "" + this.name + (this.enabled ? ' (*)' : ''); - }.property('selected', 'name', 'enabled'), - - changed: changed.property.apply(changed, trackedProperties.concat('originals')), - - startTrackingChanges: function() { - this.set('originals', this.getProperties(trackedProperties)); - }.on('init'), - - saveChanges() { - return this.save(this.getProperties(trackedProperties)).then(() => this.startTrackingChanges()); - }, - -}); - -export default SiteCustomization; diff --git a/app/assets/javascripts/admin/models/staff-action-log.js.es6 b/app/assets/javascripts/admin/models/staff-action-log.js.es6 index 24ec4fbcc0..f108dd21d8 100644 --- a/app/assets/javascripts/admin/models/staff-action-log.js.es6 +++ b/app/assets/javascripts/admin/models/staff-action-log.js.es6 @@ -39,7 +39,7 @@ const StaffActionLog = Discourse.Model.extend({ }.property('action_name'), useCustomModalForDetails: function() { - return _.contains(['change_site_customization', 'delete_site_customization'], this.get('action_name')); + return _.contains(['change_theme', 'delete_theme'], this.get('action_name')); }.property('action_name') }); diff --git a/app/assets/javascripts/admin/models/theme.js.es6 b/app/assets/javascripts/admin/models/theme.js.es6 new file mode 100644 index 0000000000..229d7ee0c0 --- /dev/null +++ b/app/assets/javascripts/admin/models/theme.js.es6 @@ -0,0 +1,114 @@ +import RestModel from 'discourse/models/rest'; +import { default as computed } from 'ember-addons/ember-computed-decorators'; + +const Theme = RestModel.extend({ + + @computed('theme_fields') + themeFields(fields) { + + if (!fields) { + this.set('theme_fields', []); + return {}; + } + + let hash = {}; + if (fields) { + fields.forEach(field=>{ + hash[field.target + " " + field.name] = field; + }); + } + return hash; + }, + + hasEdited(target, name){ + if (name) { + return !Em.isEmpty(this.getField(target, name)); + } else { + let fields = this.get("theme_fields") || []; + return fields.any(field => (field.target === target && !Em.isEmpty(field.value))); + } + }, + + getError(target, name) { + let themeFields = this.get("themeFields"); + let key = target + " " + name; + let field = themeFields[key]; + return field ? field.error : ""; + }, + + getField(target, name) { + let themeFields = this.get("themeFields"); + let key = target + " " + name; + let field = themeFields[key]; + return field ? field.value : ""; + }, + + setField(target, name, value) { + this.set("changed", true); + + let themeFields = this.get("themeFields"); + let key = target + " " + name; + let field = themeFields[key]; + if (!field) { + field = {name, target, value}; + this.theme_fields.push(field); + themeFields[key] = field; + } else { + field.value = value; + } + }, + + @computed("childThemes.@each") + child_theme_ids(childThemes) { + if (childThemes) { + return childThemes.map(theme => Ember.get(theme, "id")); + } + }, + + removeChildTheme(theme) { + const childThemes = this.get("childThemes"); + childThemes.removeObject(theme); + return this.saveChanges("child_theme_ids"); + }, + + addChildTheme(theme){ + let childThemes = this.get("childThemes"); + if (!childThemes) { + childThemes = []; + this.set('childThemes', childThemes); + } + childThemes.removeObject(theme); + childThemes.pushObject(theme); + return this.saveChanges("child_theme_ids"); + }, + + @computed('name', 'default') + description: function(name, isDefault) { + if (isDefault) { + return I18n.t('admin.customize.theme.default_name', {name: name}); + } else { + return name; + } + }, + + checkForUpdates() { + return this.save({remote_check: true}) + .then(() => this.set("changed", false)); + }, + + updateToLatest() { + return this.save({remote_update: true}) + .then(() => this.set("changed", false)); + }, + + changed: false, + + saveChanges() { + const hash = this.getProperties.apply(this, arguments); + return this.save(hash) + .then(() => this.set("changed", false)); + }, + +}); + +export default Theme; diff --git a/app/assets/javascripts/admin/routes/admin-customize-colors-show.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-colors-show.js.es6 new file mode 100644 index 0000000000..3f8bdcddcd --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-colors-show.js.es6 @@ -0,0 +1,18 @@ +export default Ember.Route.extend({ + + model(params) { + const all = this.modelFor('adminCustomize.colors'); + const model = all.findBy('id', parseInt(params.scheme_id)); + return model ? model : this.replaceWith('adminCustomize.colors.index'); + }, + + serialize(model) { + return {scheme_id: model.get('id')}; + }, + + setupController(controller, model) { + controller.set('model', model); + controller.set('allColors', this.modelFor('adminCustomize.colors')); + } +}); + diff --git a/app/assets/javascripts/admin/routes/admin-customize-colors.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-colors.js.es6 index 8a47f1ba21..043e571271 100644 --- a/app/assets/javascripts/admin/routes/admin-customize-colors.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-customize-colors.js.es6 @@ -6,9 +6,7 @@ export default Ember.Route.extend({ return ColorScheme.findAll(); }, - deactivate() { - this._super(); - this.controllerFor('adminCustomizeColors').set('selectedItem', null); - }, - + setupController(controller, model) { + controller.set("model", model); + } }); diff --git a/app/assets/javascripts/admin/routes/admin-customize-css-html-show.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-css-html-show.js.es6 deleted file mode 100644 index 7df829706f..0000000000 --- a/app/assets/javascripts/admin/routes/admin-customize-css-html-show.js.es6 +++ /dev/null @@ -1,11 +0,0 @@ -export default Ember.Route.extend({ - model(params) { - const all = this.modelFor('adminCustomizeCssHtml'); - const model = all.findBy('id', parseInt(params.site_customization_id)); - return model ? { model, section: params.section } : this.replaceWith('adminCustomizeCssHtml.index'); - }, - - setupController(controller, hash) { - controller.setProperties(hash); - } -}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-css-html.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-css-html.js.es6 deleted file mode 100644 index 5bbb460959..0000000000 --- a/app/assets/javascripts/admin/routes/admin-customize-css-html.js.es6 +++ /dev/null @@ -1,26 +0,0 @@ -import showModal from 'discourse/lib/show-modal'; -import { popupAjaxError } from 'discourse/lib/ajax-error'; - -export default Ember.Route.extend({ - model() { - return this.store.findAll('site-customization'); - }, - - actions: { - importModal() { - showModal('upload-customization'); - }, - - newCustomization(obj) { - obj = obj || {name: I18n.t("admin.customize.new_style")}; - const item = this.store.createRecord('site-customization'); - - const all = this.modelFor('adminCustomizeCssHtml'); - const self = this; - item.save(obj).then(function() { - all.pushObject(item); - self.transitionTo('adminCustomizeCssHtml.show', item.get('id'), 'css'); - }).catch(popupAjaxError); - } - } -}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-index.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-index.js.es6 index 45cb6e21fb..d8b1446dfb 100644 --- a/app/assets/javascripts/admin/routes/admin-customize-index.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-customize-index.js.es6 @@ -1,5 +1,5 @@ export default Ember.Route.extend({ beforeModel() { - this.transitionTo('adminCustomize.colors'); + this.transitionTo('adminCustomizeThemes'); } }); diff --git a/app/assets/javascripts/admin/routes/admin-customize-themes-edit.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-themes-edit.js.es6 new file mode 100644 index 0000000000..c1d3b225ff --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-themes-edit.js.es6 @@ -0,0 +1,27 @@ +export default Ember.Route.extend({ + model(params) { + const all = this.modelFor('adminCustomizeThemes'); + const model = all.findBy('id', parseInt(params.theme_id)); + return model ? { model, + target: params.target, + field_name: params.field_name + } : this.replaceWith('adminCustomizeThemes.index'); + }, + + serialize(wrapper) { + return { + model: wrapper.model, + target: wrapper.target || "common", + field_name: wrapper.field_name || "scss", + theme_id: wrapper.model.get("id") + }; + }, + + setupController(controller, wrapper) { + controller.set("model", wrapper.model); + controller.setTargetName(wrapper.target || "common"); + controller.set("fieldName", wrapper.field_name || "scss"); + this.controllerFor("adminCustomizeThemes").set("editingTheme", true); + }, + +}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-themes-index.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-themes-index.js.es6 new file mode 100644 index 0000000000..b5fc281c1e --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-themes-index.js.es6 @@ -0,0 +1,5 @@ +export default Ember.Route.extend({ + setupController() { + this.controllerFor("adminCustomizeThemes").set("editingTheme", false); + }, +}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-themes-show.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-themes-show.js.es6 new file mode 100644 index 0000000000..8e925ba6dd --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-themes-show.js.es6 @@ -0,0 +1,21 @@ +export default Ember.Route.extend({ + + serialize(model) { + return {theme_id: model.get('id')}; + }, + + model(params) { + const all = this.modelFor('adminCustomizeThemes'); + const model = all.findBy('id', parseInt(params.theme_id)); + return model ? model : this.replaceWith('adminCustomizeTheme.index'); + }, + + setupController(controller, model) { + controller.set("model", model); + const parentController = this.controllerFor("adminCustomizeThemes"); + parentController.set("editingTheme", false); + controller.set("allThemes", parentController.get("model")); + controller.set("colorSchemes", parentController.get("model.extras.color_schemes")); + controller.set("colorSchemeId", model.get("color_scheme_id")); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-themes.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-themes.js.es6 new file mode 100644 index 0000000000..8291378b05 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-themes.js.es6 @@ -0,0 +1,35 @@ +import showModal from 'discourse/lib/show-modal'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; + +export default Ember.Route.extend({ + model() { + return this.store.findAll('theme'); + }, + + setupController(controller, model) { + this._super(controller, model); + controller.set("editingTheme", false); + }, + + actions: { + importModal() { + showModal('admin-import-theme', {admin: true}); + }, + + addTheme(theme) { + const all = this.modelFor('adminCustomizeThemes'); + all.pushObject(theme); + this.transitionTo('adminCustomizeThemes.show', theme.get('id')); + }, + + + newTheme(obj) { + obj = obj || {name: I18n.t("admin.customize.new_style")}; + const item = this.store.createRecord('theme'); + + item.save(obj).then(() => { + this.send('addTheme', item); + }).catch(popupAjaxError); + } + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 b/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 index 8b19ccb0dc..698f90d77c 100644 --- a/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 @@ -13,14 +13,9 @@ export default Discourse.Route.extend({ }, showCustomDetailsModal(model) { - const modalName = (model.action_name + '_details').replace(/\_/g, "-"); - - showModal(modalName, { - model, - admin: true, - templateName: 'site-customization-change' - }); - this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal'); + let modal = showModal('admin-theme-change', { model, admin: true}); + this.controllerFor('modal').set('modalClass', 'history-modal'); + modal.loadDiff(); } } }); diff --git a/app/assets/javascripts/admin/routes/admin-reports.js.es6 b/app/assets/javascripts/admin/routes/admin-reports.js.es6 index eb89a3306e..47ece70580 100644 --- a/app/assets/javascripts/admin/routes/admin-reports.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-reports.js.es6 @@ -7,11 +7,11 @@ @module Discourse **/ export default Discourse.Route.extend({ - queryParams: { mode: {}, "start-date": {}, "end-date": {}, "category-id": {}, "group-id": {}}, + queryParams: { mode: {}, "start_date": {}, "end_date": {}, "category_id": {}, "group_id": {} }, model: function(params) { const Report = require('admin/models/report').default; - return Report.find(params.type, params['start-date'], params['end-date'], params['category-id'], params['group-id']); + return Report.find(params.type, params['start_date'], params['end_date'], params['category_id'], params['group_id']); }, setupController: function(controller, model) { diff --git a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 index bd38784bb7..dd87207156 100644 --- a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 @@ -15,10 +15,14 @@ export default function() { }); this.route('adminCustomize', { path: '/customize', resetNamespace: true } ,function() { - this.route('colors'); - this.route('adminCustomizeCssHtml', { path: 'css_html', resetNamespace: true }, function() { - this.route('show', {path: '/:site_customization_id/:section'}); + this.route('colors', function() { + this.route('show', {path: '/:scheme_id'}); + }); + + this.route('adminCustomizeThemes', { path: 'themes', resetNamespace: true }, function() { + this.route('show', {path: '/:theme_id'}); + this.route('edit', {path: '/:theme_id/:target/:field_name/edit'}); }); this.route('adminSiteText', { path: '/site_texts', resetNamespace: true }, function() { diff --git a/app/assets/javascripts/admin/templates/components/customize-link.hbs b/app/assets/javascripts/admin/templates/components/customize-link.hbs deleted file mode 100644 index dd3c4104c7..0000000000 --- a/app/assets/javascripts/admin/templates/components/customize-link.hbs +++ /dev/null @@ -1,5 +0,0 @@ -
  • - - {{customization.description}} - -
  • diff --git a/app/assets/javascripts/admin/templates/components/inline-edit-checkbox.hbs b/app/assets/javascripts/admin/templates/components/inline-edit-checkbox.hbs new file mode 100644 index 0000000000..3a651ad0df --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/inline-edit-checkbox.hbs @@ -0,0 +1,8 @@ + +{{#if changed}} + {{d-button action="finished" class="btn-primary btn-small submit-edit" icon="check"}} + {{d-button action="cancelled" class="btn-small cancel-edit" icon="times"}} +{{/if}} diff --git a/app/assets/javascripts/admin/templates/customize-colors-index.hbs b/app/assets/javascripts/admin/templates/customize-colors-index.hbs new file mode 100644 index 0000000000..62bbb7a8fc --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-colors-index.hbs @@ -0,0 +1 @@ +

    {{i18n 'admin.customize.colors.about'}}

    diff --git a/app/assets/javascripts/admin/templates/customize-colors-show.hbs b/app/assets/javascripts/admin/templates/customize-colors-show.hbs new file mode 100644 index 0000000000..6b3c8a3e91 --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-colors-show.hbs @@ -0,0 +1,62 @@ +
    +
    +

    {{#if model.theme_id}}{{model.name}}{{else}}{{text-field class="style-name" value=model.name}}{{/if}}

    +
    + {{#unless model.theme_id}} + + {{/unless}} + + + {{#if model.theme_id}} + {{i18n "admin.customize.theme_owner"}} + {{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}} + {{else}} + + {{/if}} + {{model.savingStatus}} +
    + +
    + +
    + +
    + + {{#if colors.length}} + + + + + + + + + + {{#each colors as |c|}} + + + + + + {{/each}} + +
    {{i18n 'admin.customize.color'}}
    + {{c.translatedName}} +
    + {{c.description}} +
    {{color-input hexValue=c.hex brightnessValue=c.brightness valid=c.valid}} + {{#unless model.theme_id}} + + + {{/unless}} +
    + {{else}} +

    {{i18n 'search.no_results'}}

    + {{/if}} +
    +
    diff --git a/app/assets/javascripts/admin/templates/customize-colors.hbs b/app/assets/javascripts/admin/templates/customize-colors.hbs index 62b489075d..1ed39b8fcf 100644 --- a/app/assets/javascripts/admin/templates/customize-colors.hbs +++ b/app/assets/javascripts/admin/templates/customize-colors.hbs @@ -1,78 +1,17 @@ -
    +

    {{i18n 'admin.customize.colors.long_title'}}

    - +
    -{{#if selectedItem}} -
    -
    -

    {{text-field class="style-name" value=selectedItem.name}}

    - -
    - - - - - {{selectedItem.savingStatus}} -
    - -
    - -
    - -
    - - {{#if colors.length}} - - - - - - - - - - {{#each colors as |c|}} - - - - - - {{/each}} - -
    {{i18n 'admin.customize.color'}}
    - {{c.translatedName}} -
    - {{c.description}} -
    {{color-input hexValue=c.hex brightnessValue=c.brightness valid=c.valid}} - - -
    - {{else}} -

    {{i18n 'search.no_results'}}

    - {{/if}} -
    -
    -{{else}} -

    {{i18n 'admin.customize.colors.about'}}

    -{{/if}} +{{outlet}}
    diff --git a/app/assets/javascripts/admin/templates/customize-css-html-show.hbs b/app/assets/javascripts/admin/templates/customize-css-html-show.hbs deleted file mode 100644 index 6a10c7ae0b..0000000000 --- a/app/assets/javascripts/admin/templates/customize-css-html-show.hbs +++ /dev/null @@ -1,75 +0,0 @@ -
    -
    - {{text-field class="style-name" value=model.name}} - {{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}} - -
    - -
    - -
    - {{#if cssActive}}{{ace-editor content=model.stylesheet mode="scss"}}{{/if}} - {{#if headerActive}}{{ace-editor content=model.header mode="html"}}{{/if}} - {{#if topActive}}{{ace-editor content=model.top mode="html"}}{{/if}} - {{#if footerActive}}{{ace-editor content=model.footer mode="html"}}{{/if}} - {{#if headTagActive}}{{ace-editor content=model.head_tag mode="html"}}{{/if}} - {{#if bodyTagActive}}{{ace-editor content=model.body_tag mode="html"}}{{/if}} - {{#if embeddedCssActive}}{{ace-editor content=model.embedded_css mode="css"}}{{/if}} - {{#if mobileCssActive}}{{ace-editor content=model.mobile_stylesheet mode="scss"}}{{/if}} - {{#if mobileHeaderActive}}{{ace-editor content=model.mobile_header mode="html"}}{{/if}} - {{#if mobileTopActive}}{{ace-editor content=model.mobile_top mode="html"}}{{/if}} - {{#if mobileFooterActive}}{{ace-editor content=model.mobile_footer mode="html"}}{{/if}} -
    - - -
    -
    diff --git a/app/assets/javascripts/admin/templates/customize-css-html.hbs b/app/assets/javascripts/admin/templates/customize-css-html.hbs deleted file mode 100644 index 73b8e22c9f..0000000000 --- a/app/assets/javascripts/admin/templates/customize-css-html.hbs +++ /dev/null @@ -1,13 +0,0 @@ -
    -

    {{i18n 'admin.customize.css_html.long_title'}}

    - - - {{d-button label="admin.customize.new" icon="plus" action="newCustomization" class="btn-primary"}} - {{d-button action="importModal" icon="upload" label="admin.customize.import"}} -
    - -{{outlet}} diff --git a/app/assets/javascripts/admin/templates/customize-themes-edit.hbs b/app/assets/javascripts/admin/templates/customize-themes-edit.hbs new file mode 100644 index 0000000000..b728480950 --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-themes-edit.hbs @@ -0,0 +1,80 @@ +
    +
    +

    {{i18n 'admin.customize.theme.edit_css_html'}} {{#link-to 'adminCustomizeThemes.show' model.id replace=true}}{{model.name}}{{/link-to}}

    + + {{#if error}} +
    {{error}}
    + {{/if}} + +
    + +
    + +
    +
    +
    + +
    + +
    + +
    + {{ace-editor content=activeSection editorId=editorId mode=activeSectionMode autofocus="true"}} +
    + + +
    +
    diff --git a/app/assets/javascripts/admin/templates/customize-css-html-index.hbs b/app/assets/javascripts/admin/templates/customize-themes-index.hbs similarity index 100% rename from app/assets/javascripts/admin/templates/customize-css-html-index.hbs rename to app/assets/javascripts/admin/templates/customize-themes-index.hbs diff --git a/app/assets/javascripts/admin/templates/customize-themes-show.hbs b/app/assets/javascripts/admin/templates/customize-themes-show.hbs new file mode 100644 index 0000000000..55c2c5bb39 --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-themes-show.hbs @@ -0,0 +1,121 @@ +
    +

    + {{#if editingName}} + {{text-field value=model.name autofocus="true"}} + {{d-button action="finishedEditingName" class="btn-primary btn-small submit-edit" icon="check"}} + {{d-button action="cancelEditingName" class="btn-small cancel-edit" icon="times"}} + {{else}} + {{model.name}} {{fa-icon "pencil"}} + {{/if}} +

    + + {{#if model.remote_theme}} +

    + {{i18n "admin.customize.theme.about_theme"}} +

    + {{#if model.remote_theme.license_url}} +

    + {{i18n "admin.customize.theme.license"}} {{fa-icon "copyright"}} +

    + {{/if}} + {{/if}} + + + {{#if parentThemes}} +

    {{i18n "admin.customize.theme.component_of"}}

    + + {{else}} +

    + {{inline-edit-checkbox action="applyDefault" labelKey="admin.customize.theme.is_default" checked=model.default}} + {{inline-edit-checkbox action="applyUserSelectable" labelKey="admin.customize.theme.user_selectable" checked=model.user_selectable}} +

    + +

    {{i18n "admin.customize.theme.color_scheme"}}

    +

    {{i18n "admin.customize.theme.color_scheme_select"}}

    +

    {{combo-box content=colorSchemes + nameProperty="name" + value=colorSchemeId + selectionIcon="paint-brush" + valueAttribute="id"}} + {{#if colorSchemeChanged}} + {{d-button action="changeScheme" class="btn-primary btn-small submit-edit" icon="check"}} + {{d-button action="cancelChangeScheme" class="btn-small cancel-edit" icon="times"}} + {{/if}} +

    + {{#link-to 'adminCustomize.colors' class="btn edit"}}{{i18n 'admin.customize.colors.edit'}}{{/link-to}} + {{/if}} + +

    {{i18n "admin.customize.theme.css_html"}}

    + {{#if hasEditedFields}} + +

    {{i18n "admin.customize.theme.custom_sections"}}

    + + {{else}} +

    + {{i18n "admin.customize.theme.edit_css_html_help"}} +

    + {{/if}} +

    + {{#if model.remote_theme}} + {{#if model.remote_theme.commits_behind}} + {{#d-button action="updateToLatest" icon="download" class='btn-primary'}}{{i18n "admin.customize.theme.update_to_latest"}}{{/d-button}} + {{else}} + {{#d-button action="checkForThemeUpdates" icon="refresh"}}{{i18n "admin.customize.theme.check_for_updates"}}{{/d-button}} + {{/if}} + {{/if}} + {{#d-button action="editTheme" class="btn edit"}}{{i18n 'admin.customize.theme.edit_css_html'}}{{/d-button}} + {{#if model.remote_theme}} + + {{#if updatingRemote}} + {{i18n 'admin.customize.theme.updating'}} + {{else}} + {{#if model.remote_theme.commits_behind}} + {{i18n 'admin.customize.theme.commits_behind' count=model.remote_theme.commits_behind}} + {{else}} + {{i18n 'admin.customize.theme.up_to_date'}} {{format-date model.remote_theme.updated_at leaveAgo="true"}} + {{/if}} + {{/if}} + + {{/if}} +

    + + {{#if availableChildThemes}} +

    {{i18n "admin.customize.theme.theme_components"}}

    + {{#unless model.childThemes.length}} +

    + +

    + {{else}} + + {{/unless}} + {{#if selectableChildThemes}} +

    {{combo-box content=selectableChildThemes + nameProperty="name" + value=selectedChildThemeId + valueAttribute="id"}} + + {{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}} +

    + {{/if}} + {{/if}} + + {{fa-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}} + {{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}} + + {{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}} +
    diff --git a/app/assets/javascripts/admin/templates/customize-themes.hbs b/app/assets/javascripts/admin/templates/customize-themes.hbs new file mode 100644 index 0000000000..43129006a5 --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-themes.hbs @@ -0,0 +1,24 @@ +{{#unless editingTheme}} +
    +

    {{i18n 'admin.customize.theme.long_title'}}

    + + + {{d-button label="admin.customize.new" icon="plus" action="newTheme" class="btn-primary"}} + {{d-button action="importModal" icon="upload" label="admin.customize.import"}} +
    +{{/unless}} +{{outlet}} diff --git a/app/assets/javascripts/admin/templates/customize.hbs b/app/assets/javascripts/admin/templates/customize.hbs index 7696b34811..3065c09855 100644 --- a/app/assets/javascripts/admin/templates/customize.hbs +++ b/app/assets/javascripts/admin/templates/customize.hbs @@ -1,7 +1,7 @@
    {{#admin-nav}} + {{nav-item route='adminCustomizeThemes' label='admin.customize.theme.title'}} {{nav-item route='adminCustomize.colors' label='admin.customize.colors.title'}} - {{nav-item route='adminCustomizeCssHtml' label='admin.customize.css_html.title'}} {{nav-item route='adminSiteText' label='admin.site_text.title'}} {{nav-item route='adminCustomizeEmailTemplates' label='admin.customize.email_templates.title'}} {{nav-item route='adminUserFields' label='admin.user_fields.title'}} diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index 095b3f5f4b..79dbcfc3b6 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -83,6 +83,12 @@ {{combo-box name="alias" valueAttribute="value" value=model.alias_level content=aliasLevelOptions}}
    +
    + + {{notifications-button i18nPrefix='groups.notifications' notificationLevel=model.default_notification_level}} +
    +
    + {{#unless model.automatic}}
    diff --git a/app/assets/javascripts/admin/templates/groups-bulk-complete.hbs b/app/assets/javascripts/admin/templates/groups-bulk-complete.hbs index 51eb3e4394..6a2d8b13e5 100644 --- a/app/assets/javascripts/admin/templates/groups-bulk-complete.hbs +++ b/app/assets/javascripts/admin/templates/groups-bulk-complete.hbs @@ -1 +1,11 @@ -

    {{i18n "admin.groups.bulk_complete"}}

    +{{#if bulkAddResponse}} +

    {{{bulkAddResponse.message}}}

    + {{#if bulkAddResponse.users_not_added}} +

    {{i18n "admin.groups.bulk_complete_users_not_added"}}

    + {{#each bulkAddResponse.users_not_added as |user|}} + {{user}}
    + {{/each}} + {{/if}} +{{else}} +

    {{i18n "admin.groups.bulk_complete"}}

    +{{/if}} diff --git a/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs b/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs new file mode 100644 index 0000000000..5286bbf0b0 --- /dev/null +++ b/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs @@ -0,0 +1,12 @@ +
    + {{#d-modal-body title="admin.customize.colors.select_base.title"}} + {{i18n "admin.customize.colors.select_base.description"}} + {{combo-box content=model + nameProperty="name" + value=selectedBaseThemeId + valueAttribute="base_scheme_id"}} + {{/d-modal-body}} + +
    diff --git a/app/assets/javascripts/admin/templates/modal/admin-import-theme.hbs b/app/assets/javascripts/admin/templates/modal/admin-import-theme.hbs new file mode 100644 index 0000000000..7c4058d52d --- /dev/null +++ b/app/assets/javascripts/admin/templates/modal/admin-import-theme.hbs @@ -0,0 +1,27 @@ +{{#d-modal-body class='upload-selector' title="admin.customize.theme.import_theme"}} +
    + {{radio-button name="upload" id="local" value="local" selection=selection}} + + {{#if local}} +
    +
    + {{i18n 'admin.customize.theme.import_file_tip'}} +
    + {{/if}} +
    +
    + {{radio-button name="upload" id="remote" value="remote" selection=selection}} + + {{#if remote}} +
    + {{input value=uploadUrl placeholder="https://github.com/discourse/discourse/sample_theme"}} + {{i18n 'admin.customize.theme.import_web_tip'}} +
    + {{/if}} +
    +{{/d-modal-body}} + + diff --git a/app/assets/javascripts/admin/templates/modal/admin-theme-change.hbs b/app/assets/javascripts/admin/templates/modal/admin-theme-change.hbs new file mode 100644 index 0000000000..3fbaf0ac86 --- /dev/null +++ b/app/assets/javascripts/admin/templates/modal/admin-theme-change.hbs @@ -0,0 +1,8 @@ +
    + {{#d-modal-body title="admin.logs.staff_actions.modal_title"}} + {{{diff}}} + {{/d-modal-body}} + +
    diff --git a/app/assets/javascripts/admin/templates/modal/site-customization-change.hbs b/app/assets/javascripts/admin/templates/modal/site-customization-change.hbs deleted file mode 100644 index bbacea995c..0000000000 --- a/app/assets/javascripts/admin/templates/modal/site-customization-change.hbs +++ /dev/null @@ -1,29 +0,0 @@ -
    - - {{#d-modal-body title="admin.logs.staff_actions.modal_title"}} - - - {{/d-modal-body}} - -
    diff --git a/app/assets/javascripts/admin/templates/user-index.hbs b/app/assets/javascripts/admin/templates/user-index.hbs index f172c65124..069d47ecb2 100644 --- a/app/assets/javascripts/admin/templates/user-index.hbs +++ b/app/assets/javascripts/admin/templates/user-index.hbs @@ -343,32 +343,31 @@
    -
    -

    {{i18n 'admin.groups.title'}}

    - - {{#if currentUser.admin}} -
    -
    {{i18n 'admin.groups.automatic'}}
    -
    {{automaticGroups}}
    -
    -
    -
    {{i18n 'admin.groups.custom'}}
    -
    - {{admin-group-selector selected=model.customGroups available=availableGroups}} +{{#if currentUser.admin}} +
    +

    {{i18n 'admin.groups.title'}}

    +
    +
    {{i18n 'admin.groups.automatic'}}
    +
    {{automaticGroups}}
    -
    - {{#if model.customGroups}} - {{i18n 'admin.groups.primary'}} - {{combo-box content=model.customGroups value=model.primary_group_id nameProperty="name" none="admin.groups.no_primary"}} - {{/if}} - {{#if primaryGroupDirty}} - {{d-button icon="check" class="ok" action="savePrimaryGroup"}} - {{d-button icon="times" class="cancel" action="resetPrimaryGroup"}} - {{/if}} +
    +
    {{i18n 'admin.groups.custom'}}
    +
    + {{admin-group-selector selected=model.customGroups available=availableGroups}} +
    +
    + {{#if model.customGroups}} + {{i18n 'admin.groups.primary'}} + {{combo-box content=model.customGroups value=model.primary_group_id nameProperty="name" none="admin.groups.no_primary"}} + {{/if}} + {{#if primaryGroupDirty}} + {{d-button icon="check" class="ok" action="savePrimaryGroup"}} + {{d-button icon="times" class="cancel" action="resetPrimaryGroup"}} + {{/if}} +
    -
    - {{/if}} -
    +
    +{{/if}}

    {{i18n 'admin.user.activity'}}

    diff --git a/app/assets/javascripts/discourse-common/components/combo-box.js.es6 b/app/assets/javascripts/discourse-common/components/combo-box.js.es6 index 2a2c4e2ba7..e5828e8fc7 100644 --- a/app/assets/javascripts/discourse-common/components/combo-box.js.es6 +++ b/app/assets/javascripts/discourse-common/components/combo-box.js.es6 @@ -11,28 +11,53 @@ export default Ember.Component.extend(bufferedRender({ buildBuffer(buffer) { const nameProperty = this.get('nameProperty'); const none = this.get('none'); + let noneValue = null; // Add none option if required if (typeof none === "string") { buffer.push('"); } else if (typeof none === "object") { - buffer.push(""); + noneValue = Em.get(none, this.get('valueAttribute')); + buffer.push(``); } let selected = this.get('value'); if (!Em.isNone(selected)) { selected = selected.toString(); } - if (this.get('content')) { - this.get('content').forEach(o => { + let selectedFound = false; + let firstVal = undefined; + const content = this.get('content'); + + if (content) { + let first = true; + content.forEach(o => { let val = o[this.get('valueAttribute')]; if (typeof val === "undefined") { val = o; } if (!Em.isNone(val)) { val = val.toString(); } const selectedText = (val === selected) ? "selected" : ""; const name = Handlebars.Utils.escapeExpression(Ember.get(o, nameProperty) || o); + + if (val === selected) { + selectedFound = true; + } + if (first) { + firstVal = val; + first = false; + } buffer.push(``); }); } + + if (!selectedFound && !noneValue) { + if (none) { + this.set('value', null); + } else { + this.set('value', firstVal); + } + } + + Ember.run.scheduleOnce('afterRender', this, this._updateSelect2); }, @observes('value') @@ -66,12 +91,31 @@ export default Ember.Component.extend(bufferedRender({ const $elem = this.$(); const caps = this.capabilities; - const minimumResultsForSearch = (caps && caps.isIOS) ? -1 : 5; - $elem.select2({ - formatResult: this.comboTemplate, minimumResultsForSearch, + const minimumResultsForSearch = this.get('minimumResultsForSearch') || ((caps && caps.isIOS) ? -1 : 5); + + if (!this.get("selectionTemplate") && this.get("selectionIcon")) { + this.selectionTemplate = (item) => { + let name = Em.get(item, 'text'); + name = Handlebars.escapeExpression(name); + return `${name}`; + }; + } + + const options = { + minimumResultsForSearch, width: this.get('width') || 'resolve', allowClear: true - }); + }; + + if (this.comboTemplate) { + options.formatResult = this.comboTemplate.bind(this); + } + + if (this.selectionTemplate) { + options.formatSelection = this.selectionTemplate.bind(this); + } + + $elem.select2(options); const castInteger = this.get('castInteger'); $elem.on("change", e => { @@ -81,9 +125,14 @@ export default Ember.Component.extend(bufferedRender({ } this.set('value', val); }); + Ember.run.scheduleOnce('afterRender', this, this._triggerChange); }, + _updateSelect2() { + this.$().trigger('change.select2'); + }, + _triggerChange() { this.$().trigger('change'); }, diff --git a/app/assets/javascripts/discourse/adapters/rest.js.es6 b/app/assets/javascripts/discourse/adapters/rest.js.es6 index 72d05fb7dd..b56ac1628a 100644 --- a/app/assets/javascripts/discourse/adapters/rest.js.es6 +++ b/app/assets/javascripts/discourse/adapters/rest.js.es6 @@ -1,7 +1,7 @@ import { ajax } from 'discourse/lib/ajax'; import { hashString } from 'discourse/lib/hash'; -const ADMIN_MODELS = ['plugin', 'site-customization', 'embeddable-host', 'web-hook', 'web-hook-event']; +const ADMIN_MODELS = ['plugin', 'theme', 'embeddable-host', 'web-hook', 'web-hook-event']; export function Result(payload, responseJson) { this.payload = payload; @@ -76,22 +76,38 @@ export default Ember.Object.extend({ this.cached[this.storageKey(type,findArgs,opts)] = hydrated; }, + jsonMode: false, + + getPayload(method, data) { + let payload = {method, data}; + + if (this.jsonMode) { + payload.contentType = "application/json"; + payload.data = JSON.stringify(data); + } + + return payload; + }, + update(store, type, id, attrs) { const data = {}; const typeField = Ember.String.underscore(type); data[typeField] = attrs; - return ajax(this.pathFor(store, type, id), { method: 'PUT', data }).then(function(json) { - return new Result(json[typeField], json); - }); + + return ajax(this.pathFor(store, type, id), this.getPayload('PUT', data)) + .then(function(json) { + return new Result(json[typeField], json); + }); }, createRecord(store, type, attrs) { const data = {}; const typeField = Ember.String.underscore(type); data[typeField] = attrs; - return ajax(this.pathFor(store, type), { method: 'POST', data }).then(function (json) { - return new Result(json[typeField], json); - }); + return ajax(this.pathFor(store, type), this.getPayload('POST', data)) + .then(function (json) { + return new Result(json[typeField], json); + }); }, destroyRecord(store, type, record) { diff --git a/app/assets/javascripts/discourse/components/auto-update-input-selector.js.es6 b/app/assets/javascripts/discourse/components/auto-update-input-selector.js.es6 new file mode 100644 index 0000000000..353c6951cc --- /dev/null +++ b/app/assets/javascripts/discourse/components/auto-update-input-selector.js.es6 @@ -0,0 +1,172 @@ +import { default as computed, observes } from "ember-addons/ember-computed-decorators"; +import Combobox from 'discourse-common/components/combo-box'; +import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-status-update'; + +const LATER_TODAY = 'later_today'; +const TOMORROW = 'tomorrow'; +const LATER_THIS_WEEK = 'later_this_week'; +const THIS_WEEKEND = 'this_weekend'; +const NEXT_WEEK = 'next_week'; +export const PICK_DATE_AND_TIME = 'pick_date_and_time'; +export const SET_BASED_ON_LAST_POST = 'set_based_on_last_post'; + +export const FORMAT = 'YYYY-MM-DD HH:mm'; + +export default Combobox.extend({ + classNames: ['auto-update-input-selector'], + isCustom: Ember.computed.equal("value", PICK_DATE_AND_TIME), + + @computed() + content() { + const selections = []; + const now = moment(); + const canScheduleToday = (24 - now.hour()) > 6; + const day = now.day(); + + if (canScheduleToday) { + selections.push({ + id: LATER_TODAY, + name: I18n.t('topic.auto_update_input.later_today') + }); + } + + selections.push({ + id: TOMORROW, + name: I18n.t('topic.auto_update_input.tomorrow') + }); + + if (!canScheduleToday && day < 4) { + selections.push({ + id: LATER_THIS_WEEK, + name: I18n.t('topic.auto_update_input.later_this_week') + }); + } + + if (day < 5) { + selections.push({ + id: THIS_WEEKEND, + name: I18n.t('topic.auto_update_input.this_weekend') + }); + } + + + if (day !== 7) { + selections.push({ + id: NEXT_WEEK, + name: I18n.t('topic.auto_update_input.next_week') + }); + } + + selections.push({ + id: PICK_DATE_AND_TIME, + name: I18n.t('topic.auto_update_input.pick_date_and_time') + }); + + if (this.get('statusType') === CLOSE_STATUS_TYPE) { + selections.push({ + id: SET_BASED_ON_LAST_POST, + name: I18n.t('topic.auto_update_input.set_based_on_last_post') + }); + } + + return selections; + }, + + @observes('value') + _updateInput() { + if (this.get('isCustom')) return; + let input = null; + const { time } = this.get('updateAt'); + + if (time && !Ember.isEmpty(this.get('value'))) { + input = time.format(FORMAT); + } + + this.set('input', input); + }, + + @computed('value') + updateAt(value) { + return this._updateAt(value); + }, + + comboTemplate(state) { + return this._format(state); + }, + + selectionTemplate(state) { + return this._format(state); + }, + + _format(state) { + let { time, icon } = this._updateAt(state.id); + let icons; + + if (icon) { + icons = icon.split(',').map(i => { + return ``; + }).join(" "); + } + + if (time) { + if (state.id === LATER_TODAY) { + time = time.format('hh:mm a'); + } else { + time = time.format('ddd, hh:mm a'); + } + } + + let output = ""; + + if (!Ember.isEmpty(icons)) { + output += `${icons}`; + } + + output += `${state.text}`; + + if (time) { + output += `${time}`; + } + + return output; + }, + + _updateAt(selection) { + let time = moment(); + let icon; + const timeOfDay = this.get('statusType') !== CLOSE_STATUS_TYPE ? 8 : 18; + + switch(selection) { + case LATER_TODAY: + time = time.hour(18).minute(0); + icon = 'desktop'; + break; + case TOMORROW: + time = time.add(1, 'day').hour(timeOfDay).minute(0); + icon = 'sun-o'; + break; + case LATER_THIS_WEEK: + time = time.add(2, 'day').hour(timeOfDay).minute(0); + icon = 'briefcase'; + break; + case THIS_WEEKEND: + time = time.day(6).hour(timeOfDay).minute(0); + icon = 'bed'; + break; + case NEXT_WEEK: + time = time.add(1, 'week').day(1).hour(timeOfDay).minute(0); + icon = 'briefcase'; + break; + case PICK_DATE_AND_TIME: + time = null; + icon = 'calendar-plus-o'; + break; + case SET_BASED_ON_LAST_POST: + time = null; + icon = 'clock-o'; + break; + } + + return { time, icon }; + }, +}); diff --git a/app/assets/javascripts/discourse/components/auto-update-input.js.es6 b/app/assets/javascripts/discourse/components/auto-update-input.js.es6 index 1d44119778..747c061793 100644 --- a/app/assets/javascripts/discourse/components/auto-update-input.js.es6 +++ b/app/assets/javascripts/discourse/components/auto-update-input.js.es6 @@ -1,47 +1,92 @@ import { default as computed, observes } from "ember-addons/ember-computed-decorators"; +import { + FORMAT, + PICK_DATE_AND_TIME, + SET_BASED_ON_LAST_POST +} from "discourse/components/auto-update-input-selector"; export default Ember.Component.extend({ - limited: false, + selection: null, + date: null, + time: null, + isCustom: Ember.computed.equal('selection', PICK_DATE_AND_TIME), + isBasedOnLastPost: Ember.computed.equal('selection', SET_BASED_ON_LAST_POST), - didInsertElement() { + init() { this._super(); - this._updateInputValid(); - }, - @computed("limited") - inputUnitsKey(limited) { - return limited ? "topic.auto_update_input.limited.units" : "topic.auto_update_input.all.units"; - }, + const input = this.get('input'); - @computed("limited") - inputExamplesKey(limited) { - return limited ? "topic.auto_update_input.limited.examples" : "topic.auto_update_input.all.examples"; - }, - - @observes("input", "limited") - _updateInputValid() { - this.set( - "inputValid", this._isInputValid(this.get("input"), this.get("limited")) - ); - }, - - _isInputValid(input, limited) { - const t = (input || "").toString().trim(); - - if (t.length === 0) { - return true; - // "empty" is always valid - } else if (limited) { - // only # of hours in limited mode - return t.match(/^(\d+\.)?\d+$/); - } else { - if (t.match(/^\d{4}-\d{1,2}-\d{1,2}(?: \d{1,2}:\d{2}(\s?[AP]M)?){0,1}$/i)) { - // timestamp must be in the future - return moment(t).isAfter(); + if (input) { + if (this.get('basedOnLastPost')) { + this.set('selection', SET_BASED_ON_LAST_POST); } else { - // either # of hours or absolute time - return (t.match(/^(\d+\.)?\d+$/) || t.match(/^\d{1,2}:\d{2}(\s?[AP]M)?$/i)) !== null; + this.set('selection', PICK_DATE_AND_TIME); + const datetime = moment(input); + this.set('date', datetime.toDate()); + this.set('time', datetime.format("HH:mm")); + this._updateInput(); } } - } + }, + + @observes("date", "time") + _updateInput() { + const date = moment(this.get('date')).format("YYYY-MM-DD"); + const time = (this.get('time') && ` ${this.get('time')}`) || ''; + this.set('input', moment(`${date}${time}`).format(FORMAT)); + }, + + @observes("isBasedOnLastPost") + _updateBasedOnLastPost() { + this.set('basedOnLastPost', this.get('isBasedOnLastPost')); + }, + + @computed("input", "isBasedOnLastPost") + duration(input, isBasedOnLastPost) { + const now = moment(); + + if (isBasedOnLastPost) { + return parseFloat(input); + } else { + return moment(input) - now; + } + }, + + @computed("input", "isBasedOnLastPost") + executeAt(input, isBasedOnLastPost) { + if (isBasedOnLastPost) { + return moment().add(input, 'hours').format(FORMAT); + } else { + return input; + } + }, + + @computed("statusType", "input", "isCustom", "date", "time", "willCloseImmediately") + showTopicStatusInfo(statusType, input, isCustom, date, time, willCloseImmediately) { + if (!statusType || willCloseImmediately) return false; + + if (isCustom) { + return date || time; + } else { + return input; + } + }, + + @computed('isBasedOnLastPost', 'input', 'lastPostedAt') + willCloseImmediately(isBasedOnLastPost, input, lastPostedAt) { + if (isBasedOnLastPost && input) { + let closeDate = moment(lastPostedAt); + closeDate = closeDate.add(input, 'hours'); + return closeDate < moment(); + } + }, + + @computed('isBasedOnLastPost', 'lastPostedAt') + willCloseI18n(isBasedOnLastPost, lastPostedAt) { + if (isBasedOnLastPost) { + const diff = Math.round((new Date() - new Date(lastPostedAt)) / (1000*60*60)); + return I18n.t('topic.auto_close_immediate', { count: diff }); + } + }, }); diff --git a/app/assets/javascripts/discourse/components/badge-card.js.es6 b/app/assets/javascripts/discourse/components/badge-card.js.es6 index b162e982ce..a5eb0561aa 100644 --- a/app/assets/javascripts/discourse/components/badge-card.js.es6 +++ b/app/assets/javascripts/discourse/components/badge-card.js.es6 @@ -1,37 +1,14 @@ import computed from 'ember-addons/ember-computed-decorators'; -import DiscourseURL from 'discourse/lib/url'; import { sanitize, emojiUnescape } from 'discourse/lib/text'; export default Ember.Component.extend({ size: 'medium', - classNameBindings: [':badge-card', 'size', 'badge.slug', 'navigateOnClick:hyperlink'], - - click(e){ - if (e.target && e.target.nodeName === "A") { - return true; - } - - if (!this.get('navigateOnClick')) { - return false; - } - - var url = this.get('badge.url'); - const username = this.get('username'); - if (username) { - url = url + "?username=" + encodeURIComponent(username); - } - DiscourseURL.routeTo(url); - return true; - }, + classNameBindings: [':badge-card', 'size', 'badge.slug'], @computed('count', 'badge.grant_count') displayCount(count, grantCount) { - if (count == null) { - return grantCount; - } - if (count > 1) { - return count; - } + if (count == null) { return grantCount; } + if (count > 1) { return count; } }, @computed('size') diff --git a/app/assets/javascripts/discourse/components/category-chooser.js.es6 b/app/assets/javascripts/discourse/components/category-chooser.js.es6 index b79e8ef188..d0b8b45009 100644 --- a/app/assets/javascripts/discourse/components/category-chooser.js.es6 +++ b/app/assets/javascripts/discourse/components/category-chooser.js.es6 @@ -1,11 +1,11 @@ -import ComboboxView from 'discourse-common/components/combo-box'; +import Combobox from 'discourse-common/components/combo-box'; import { categoryBadgeHTML } from 'discourse/helpers/category-link'; import computed from 'ember-addons/ember-computed-decorators'; import { observes, on } from 'ember-addons/ember-computed-decorators'; import PermissionType from 'discourse/models/permission-type'; import Category from 'discourse/models/category'; -export default ComboboxView.extend({ +export default Combobox.extend({ classNames: ['combobox category-combobox'], dataAttributes: ['id', 'description_text'], overrideWidths: true, diff --git a/app/assets/javascripts/discourse/components/composer-title.js.es6 b/app/assets/javascripts/discourse/components/composer-title.js.es6 index 91cdf7186c..0c173bdd0c 100644 --- a/app/assets/javascripts/discourse/components/composer-title.js.es6 +++ b/app/assets/javascripts/discourse/components/composer-title.js.es6 @@ -45,7 +45,7 @@ export default Ember.Component.extend({ @observes('composer.replyLength') _clearFeaturedLink() { - if (this.get('watchForLink') && this.get('composer.replyLength') === 0) { + if (this.get('watchForLink') && this.bodyIsDefault()) { this.set('composer.featuredLink', null); } }, @@ -53,7 +53,7 @@ export default Ember.Component.extend({ _checkForUrl() { if (!this.element || this.isDestroying || this.isDestroyed) { return; } - if (this.get('isAbsoluteUrl') && (this.get('composer.reply')||"").length === 0) { + if (this.get('isAbsoluteUrl') && this.bodyIsDefault()) { // only feature links to external sites if (this.get('composer.title').match(new RegExp("^https?:\\/\\/" + window.location.hostname, "i"))) { return; } @@ -88,9 +88,10 @@ export default Ember.Component.extend({ this.set('composer.featuredLink', this.get('composer.title')); const $h = $(html), - heading = $h.find('h3').length > 0 ? $h.find('h3') : $h.find('h4'); + heading = $h.find('h3').length > 0 ? $h.find('h3') : $h.find('h4'), + composer = this.get('composer'); - this.set('composer.reply', this.get('composer.title')); + composer.appendText(this.get('composer.title'), null, {block: true}); if (heading.length > 0 && heading.text().length > 0) { this.changeTitle(heading.text()); @@ -109,8 +110,13 @@ export default Ember.Component.extend({ } }, - @computed('composer.title') - isAbsoluteUrl() { - return this.get('composer.titleLength') > 0 && /^(https?:)?\/\/[\w\.\-]+/i.test(this.get('composer.title')); + @computed('composer.title', 'composer.titleLength') + isAbsoluteUrl(title, titleLength) { + return titleLength > 0 && /^(https?:)?\/\/[\w\.\-]+/i.test(title); + }, + + bodyIsDefault() { + const reply = this.get('composer.reply')||""; + return (reply.length === 0 || (reply === (this.get("composer.category.topic_template")||""))); } }); diff --git a/app/assets/javascripts/discourse/components/date-picker-future.js.es6 b/app/assets/javascripts/discourse/components/date-picker-future.js.es6 index fa6ed4037e..249c1a57d6 100644 --- a/app/assets/javascripts/discourse/components/date-picker-future.js.es6 +++ b/app/assets/javascripts/discourse/components/date-picker-future.js.es6 @@ -5,7 +5,8 @@ export default DatePicker.extend({ _opts() { return { - defaultDate: moment().add(1, "day").toDate(), + defaultDate: this.get('defaultDate') || moment().add(1, "day").toDate(), + setDefaultDate: !!this.get('defaultDate'), minDate: new Date(), }; } diff --git a/app/assets/javascripts/discourse/components/date-picker.js.es6 b/app/assets/javascripts/discourse/components/date-picker.js.es6 index de36036616..d2667dd863 100644 --- a/app/assets/javascripts/discourse/components/date-picker.js.es6 +++ b/app/assets/javascripts/discourse/components/date-picker.js.es6 @@ -1,6 +1,6 @@ /* global Pikaday:true */ import loadScript from "discourse/lib/load-script"; -import { on } from "ember-addons/ember-computed-decorators"; +import { default as computed, on } from "ember-addons/ember-computed-decorators"; export default Em.Component.extend({ classNames: ["date-picker-wrapper"], @@ -39,6 +39,11 @@ export default Em.Component.extend({ this._picker = null; }, + @computed() + placeholder() { + return I18n.t("dates.placeholder"); + }, + _opts() { return null; } diff --git a/app/assets/javascripts/discourse/components/group-notifications-button.js.es6 b/app/assets/javascripts/discourse/components/group-notifications-button.js.es6 index 8f70ca102a..ef2386086f 100644 --- a/app/assets/javascripts/discourse/components/group-notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/components/group-notifications-button.js.es6 @@ -6,6 +6,6 @@ export default NotificationsButton.extend({ i18nPrefix: 'groups.notifications', clicked(id) { - this.get('group').setNotification(id); + this.get('group').setNotification(id, this.get('user.id')); } }); diff --git a/app/assets/javascripts/discourse/components/json-file-uploader.js.es6 b/app/assets/javascripts/discourse/components/json-file-uploader.js.es6 deleted file mode 100644 index e29491d48a..0000000000 --- a/app/assets/javascripts/discourse/components/json-file-uploader.js.es6 +++ /dev/null @@ -1,103 +0,0 @@ - -export default Em.Component.extend({ - fileInput: null, - loading: false, - expectedRootObjectName: null, - hover: 0, - - classNames: ['json-uploader'], - - _initialize: function() { - const $this = this.$(); - const self = this; - - const $fileInput = $this.find('#js-file-input'); - this.set('fileInput', $fileInput[0]); - - $fileInput.on('change', function() { - self.fileSelected(this.files); - }); - - $this.on('dragover', function(e) { - if (e.preventDefault) e.preventDefault(); - return false; - }); - $this.on('dragenter', function(e) { - if (e.preventDefault) e.preventDefault(); - self.set('hover', self.get('hover') + 1); - return false; - }); - $this.on('dragleave', function(e) { - if (e.preventDefault) e.preventDefault(); - self.set('hover', self.get('hover') - 1); - return false; - }); - $this.on('drop', function(e) { - if (e.preventDefault) e.preventDefault(); - - self.set('hover', 0); - self.fileSelected(e.dataTransfer.files); - return false; - }); - - }.on('didInsertElement'), - - accept: function() { - return ".json,application/json,application/x-javascript,text/json" + (this.get('extension') ? "," + this.get('extension') : ""); - }.property('extension'), - - setReady: function() { - let parsed; - try { - parsed = JSON.parse(this.get('value')); - } catch (e) { - this.set('ready', false); - return; - } - - const rootObject = parsed[this.get('expectedRootObjectName')]; - - if (rootObject !== null && rootObject !== undefined) { - this.set('ready', true); - } else { - this.set('ready', false); - } - }.observes('destination', 'expectedRootObjectName'), - - actions: { - selectFile: function() { - const $fileInput = $(this.get('fileInput')); - $fileInput.click(); - } - }, - - fileSelected(fileList) { - const self = this; - let files = []; - for (let i = 0; i < fileList.length; i++) { - files[i] = fileList[i]; - } - const fileNameRegex = /\.(json|txt)$/; - files = files.filter(function(file) { - if (fileNameRegex.test(file.name)) { - return true; - } - if (file.type === "text/plain") { - return true; - } - return false; - }); - const firstFile = fileList[0]; - - this.set('loading', true); - - let reader = new FileReader(); - reader.onload = function(evt) { - self.set('value', evt.target.result); - self.set('loading', false); - }; - - reader.readAsText(firstFile); - } - -}); diff --git a/app/assets/javascripts/discourse/components/notifications-button.js.es6 b/app/assets/javascripts/discourse/components/notifications-button.js.es6 index 10f5d54f26..d01f17e43d 100644 --- a/app/assets/javascripts/discourse/components/notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/components/notifications-button.js.es6 @@ -43,7 +43,7 @@ export default DropdownButton.extend({ } }, - clicked(/* id */) { - // sub-class needs to implement this + clicked(id) { + this.set("notificationLevel", id); } }); diff --git a/app/assets/javascripts/discourse/components/search-advanced-options.js.es6 b/app/assets/javascripts/discourse/components/search-advanced-options.js.es6 index dd6910d18a..0bc886a817 100644 --- a/app/assets/javascripts/discourse/components/search-advanced-options.js.es6 +++ b/app/assets/javascripts/discourse/components/search-advanced-options.js.es6 @@ -143,12 +143,14 @@ export default Em.Component.extend({ matchRegEx = matchRegEx || replaceRegEx; const match = this.filterBlocks(matchRegEx); + let val = this.get(key); + if (match.length !== 0) { const userInput = match[0].replace(replaceRegEx, ''); - if (this.get(key) !== userInput) { + if (val !== userInput) { this.set(key, userInput); } - } else if(this.get(key).length !== 0) { + } else if(val && val.length !== 0) { this.set(key, ''); } }, diff --git a/app/assets/javascripts/discourse/components/topic-notifications-button.js.es6 b/app/assets/javascripts/discourse/components/topic-notifications-button.js.es6 index e39f45fd9b..b53717855d 100644 --- a/app/assets/javascripts/discourse/components/topic-notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-notifications-button.js.es6 @@ -9,12 +9,14 @@ export default MountWidget.extend({ }, @observes('topic.details.notification_level') - _triggerRerender() { - this.queueRerender(); + _triggerEvent() { + this.appEvents.trigger('topic-notifications-button:changed', { + type: 'notification', id: this.get('topic.details.notification_level') + }); }, didInsertElement() { this._super(); - this.dispatch('topic-notifications-button:keyboard-trigger', 'topic-notifications-button'); + this.dispatch('topic-notifications-button:changed', 'topic-notifications-button'); } }); diff --git a/app/assets/javascripts/discourse/components/topic-status-info.js.es6 b/app/assets/javascripts/discourse/components/topic-status-info.js.es6 index a7b5d784b0..8cb29a0b21 100644 --- a/app/assets/javascripts/discourse/components/topic-status-info.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-status-info.js.es6 @@ -2,21 +2,21 @@ import { bufferedRender } from 'discourse-common/lib/buffered-render'; import Category from 'discourse/models/category'; export default Ember.Component.extend(bufferedRender({ - elementId: 'topic-status-info', + classNames: ['topic-status-info'], delayedRerender: null, rerenderTriggers: [ - 'topic.topic_status_update', - 'topic.topic_status_update.execute_at', - 'topic.topic_status_update.based_on_last_post', - 'topic.topic_status_update.duration', - 'topic.topic_status_update.category_id', + 'statusType', + 'executeAt', + 'basedOnLastPost', + 'duration', + 'categoryId', ], buildBuffer(buffer) { - if (!this.get('topic.topic_status_update.execute_at')) return; + if (!this.get('executeAt')) return; - let statusUpdateAt = moment(this.get('topic.topic_status_update.execute_at')); + let statusUpdateAt = moment(this.get('executeAt')); if (statusUpdateAt < new Date()) return; let duration = moment.duration(statusUpdateAt - moment()); @@ -33,7 +33,7 @@ export default Ember.Component.extend(bufferedRender({ rerenderDelay = 60000; } - let autoCloseHours = this.get("topic.topic_status_update.duration") || 0; + let autoCloseHours = this.get("duration") || 0; buffer.push('

    '); @@ -42,7 +42,7 @@ export default Ember.Component.extend(bufferedRender({ duration: moment.duration(autoCloseHours, "hours").humanize(), }; - const categoryId = this.get('topic.topic_status_update.category_id'); + const categoryId = this.get('categoryId'); if (categoryId) { const category = Category.findById(categoryId); @@ -67,9 +67,9 @@ export default Ember.Component.extend(bufferedRender({ }, _noticeKey() { - const statusType = this.get('topic.topic_status_update.status_type'); + const statusType = this.get('statusType'); - if (this.get("topic.topic_status_update.based_on_last_post")) { + if (this.get("basedOnLastPost")) { return `topic.status_update_notice.auto_${statusType}_based_on_last_post`; } else { return `topic.status_update_notice.auto_${statusType}`; diff --git a/app/assets/javascripts/discourse/components/topic-timeline.js.es6 b/app/assets/javascripts/discourse/components/topic-timeline.js.es6 index eaa065857c..e2b493e45e 100644 --- a/app/assets/javascripts/discourse/components/topic-timeline.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-timeline.js.es6 @@ -79,6 +79,6 @@ export default MountWidget.extend(Docking, { } this.dispatch('topic:current-post-scrolled', 'timeline-scrollarea'); - this.dispatch('topic-notifications-button:keyboard-trigger', 'topic-notifications-button'); + this.dispatch('topic-notifications-button:changed', 'topic-notifications-button'); } }); diff --git a/app/assets/javascripts/discourse/controllers/composer.js.es6 b/app/assets/javascripts/discourse/controllers/composer.js.es6 index b40f7efdc8..19255347a2 100644 --- a/app/assets/javascripts/discourse/controllers/composer.js.es6 +++ b/app/assets/javascripts/discourse/controllers/composer.js.es6 @@ -695,15 +695,21 @@ export default Ember.Controller.extend({ return new Ember.RSVP.Promise(function (resolve) { if (self.get('model.hasMetaData') || self.get('model.replyDirty')) { - bootbox.confirm(I18n.t("post.abandon.confirm"), I18n.t("post.abandon.no_value"), - I18n.t("post.abandon.yes_value"), function(result) { - if (result) { - self.destroyDraft(); - self.get('model').clearState(); - self.close(); - resolve(); + bootbox.dialog(I18n.t("post.abandon.confirm"), [ + { label: I18n.t("post.abandon.no_value") }, + { + label: I18n.t("post.abandon.yes_value"), + 'class': 'btn-danger', + callback(result) { + if (result) { + self.destroyDraft(); + self.get('model').clearState(); + self.close(); + resolve(); + } + } } - }); + ]); } else { // it is possible there is some sort of crazy draft with no body ... just give up on it self.destroyDraft(); diff --git a/app/assets/javascripts/discourse/controllers/edit-topic-status-update.js.es6 b/app/assets/javascripts/discourse/controllers/edit-topic-status-update.js.es6 index 3dbeddeca5..bde5384560 100644 --- a/app/assets/javascripts/discourse/controllers/edit-topic-status-update.js.es6 +++ b/app/assets/javascripts/discourse/controllers/edit-topic-status-update.js.es6 @@ -3,16 +3,11 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; import TopicStatusUpdate from 'discourse/models/topic-status-update'; import { popupAjaxError } from 'discourse/lib/ajax-error'; -const CLOSE_STATUS_TYPE = 'close'; +export const CLOSE_STATUS_TYPE = 'close'; const OPEN_STATUS_TYPE = 'open'; const PUBLISH_TO_CATEGORY_STATUS_TYPE = 'publish_to_category'; export default Ember.Controller.extend(ModalFunctionality, { - closeStatusType: CLOSE_STATUS_TYPE, - openStatusType: OPEN_STATUS_TYPE, - publishToCategoryStatusType: PUBLISH_TO_CATEGORY_STATUS_TYPE, - updateTimeValid: null, - updateTimeInvalid: Em.computed.not('updateTimeValid'), loading: false, updateTime: null, topicStatusUpdate: Ember.computed.alias("model.topic_status_update"), @@ -21,42 +16,18 @@ export default Ember.Controller.extend(ModalFunctionality, { autoClose: Ember.computed.equal('selection', CLOSE_STATUS_TYPE), publishToCategory: Ember.computed.equal('selection', PUBLISH_TO_CATEGORY_STATUS_TYPE), - @computed('autoClose', 'updateTime') - disableAutoClose(autoClose, updateTime) { - return updateTime && !autoClose; + @computed("model.closed") + statusUpdates(closed) { + return [ + { id: CLOSE_STATUS_TYPE, name: I18n.t(closed ? 'topic.temp_open.title' : 'topic.auto_close.title'), }, + { id: OPEN_STATUS_TYPE, name: I18n.t(closed ? 'topic.auto_reopen.title' : 'topic.temp_close.title') }, + { id: PUBLISH_TO_CATEGORY_STATUS_TYPE, name: I18n.t('topic.publish_to_category.title') } + ]; }, - @computed('autoOpen', 'updateTime') - disableAutoOpen(autoOpen, updateTime) { - return updateTime && !autoOpen; - }, - - @computed('publishToCatgory', 'updateTime') - disablePublishToCategory(publishToCatgory, updateTime) { - return updateTime && !publishToCatgory; - }, - - @computed('topicStatusUpdate.based_on_last_post', 'updateTime', 'model.last_posted_at') - willCloseImmediately(basedOnLastPost, updateTime, lastPostedAt) { - if (!basedOnLastPost) { - return false; - } - const closeDate = new Date(lastPostedAt); - closeDate.setHours(closeDate.getHours() + updateTime); - return closeDate < new Date(); - }, - - @computed('topicStatusUpdate.based_on_last_post', 'model.last_posted_at') - willCloseI18n(basedOnLastPost, lastPostedAt) { - if (basedOnLastPost) { - const diff = Math.round((new Date() - new Date(lastPostedAt)) / (1000*60*60)); - return I18n.t('topic.auto_close_immediate', { count: diff }); - } - }, - - @computed('updateTime', 'updateTimeInvalid', 'loading') - saveDisabled(updateTime, updateTimeInvalid, loading) { - return Ember.isEmpty(updateTime) || updateTimeInvalid || loading; + @computed('updateTime', 'loading') + saveDisabled(updateTime, loading) { + return Ember.isEmpty(updateTime) || loading; }, @computed("model.visible") @@ -66,29 +37,31 @@ export default Ember.Controller.extend(ModalFunctionality, { @observes("topicStatusUpdate.execute_at", "topicStatusUpdate.duration") _setUpdateTime() { + if (!this.get('topicStatusUpdate.execute_at')) return; + let time = null; if (this.get("topicStatusUpdate.based_on_last_post")) { time = this.get("topicStatusUpdate.duration"); } else if (this.get("topicStatusUpdate.execute_at")) { - const closeTime = new Date(this.get("topicStatusUpdate.execute_at")); + const closeTime = moment(this.get('topicStatusUpdate.execute_at')); - if (closeTime > new Date()) { - time = moment(closeTime).format("YYYY-MM-DD HH:mm"); + if (closeTime > moment()) { + time = closeTime.format("YYYY-MM-DD HH:mm"); } } this.set("updateTime", time); }, - _setStatusUpdate(time, status_type) { + _setStatusUpdate(time, statusType) { this.set('loading', true); TopicStatusUpdate.updateStatus( this.get('model.id'), time, this.get('topicStatusUpdate.based_on_last_post'), - status_type, + statusType, this.get('categoryId') ).then(result => { if (time) { @@ -102,8 +75,11 @@ export default Ember.Controller.extend(ModalFunctionality, { this.set('model.closed', result.closed); } else { - this.set('topicStatusUpdate', Ember.Object.create({})); - this.set('selection', null); + this.setProperties({ + topicStatusUpdate: Ember.Object.create({}), + selection: null, + updateTime: null + }); } }).catch(error => { popupAjaxError(error); diff --git a/app/assets/javascripts/discourse/controllers/preferences.js.es6 b/app/assets/javascripts/discourse/controllers/preferences.js.es6 index 5ee945d4da..ac848d3ea2 100644 --- a/app/assets/javascripts/discourse/controllers/preferences.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences.js.es6 @@ -1,12 +1,23 @@ import { setting } from 'discourse/lib/computed'; import CanCheckEmails from 'discourse/mixins/can-check-emails'; import { popupAjaxError } from 'discourse/lib/ajax-error'; -import computed from "ember-addons/ember-computed-decorators"; +import { default as computed, observes } from "ember-addons/ember-computed-decorators"; import { cook } from 'discourse/lib/text'; import { NotificationLevels } from 'discourse/lib/notification-levels'; +import { listThemes, selectDefaultTheme, previewTheme } from 'discourse/lib/theme-selector'; export default Ember.Controller.extend(CanCheckEmails, { + userSelectableThemes: function(){ + return listThemes(this.site); + }.property(), + + @observes("selectedTheme") + themeKeyChanged() { + let key = this.get("selectedTheme"); + previewTheme(key); + }, + @computed("model.watchedCategories", "model.trackedCategories", "model.mutedCategories") selectedCategories(watched, tracked, muted) { return [].concat(watched, tracked, muted); @@ -113,7 +124,8 @@ export default Ember.Controller.extend(CanCheckEmails, { { name: I18n.t('user.auto_track_options.after_10_minutes'), value: 600000 }], notificationLevelsForReplying: [{ name: I18n.t('topic.notifications.watching.title'), value: NotificationLevels.WATCHING }, - { name: I18n.t('topic.notifications.tracking.title'), value: NotificationLevels.TRACKING }], + { name: I18n.t('topic.notifications.tracking.title'), value: NotificationLevels.TRACKING }, + { name: I18n.t('topic.notifications.regular.title'), value: NotificationLevels.REGULAR }], considerNewTopicOptions: [{ name: I18n.t('user.new_topic_duration.not_viewed'), value: -1 }, @@ -162,6 +174,7 @@ export default Ember.Controller.extend(CanCheckEmails, { Discourse.User.currentProp('name', model.get('name')); } model.set('bio_cooked', cook(model.get('bio_raw'))); + selectDefaultTheme(this.get('selectedTheme')); this.set('saved', true); }).catch(popupAjaxError); }, diff --git a/app/assets/javascripts/discourse/controllers/upload-customization.js.es6 b/app/assets/javascripts/discourse/controllers/upload-customization.js.es6 deleted file mode 100644 index ae7575887a..0000000000 --- a/app/assets/javascripts/discourse/controllers/upload-customization.js.es6 +++ /dev/null @@ -1,30 +0,0 @@ -import ModalFunctionality from 'discourse/mixins/modal-functionality'; - -export default Ember.Controller.extend(ModalFunctionality, { - notReady: Em.computed.not('ready'), - adminCustomizeCssHtml: Ember.inject.controller(), - - ready: function() { - try { - const parsed = JSON.parse(this.get('customizationFile')); - return !!parsed["site_customization"]; - } catch (e) { - return false; - } - }.property('customizationFile'), - - actions: { - createCustomization: function() { - const object = JSON.parse(this.get('customizationFile')).site_customization; - - // Slight fixup before creating object - object.enabled = false; - delete object.id; - delete object.key; - - const controller = this.get('adminCustomizeCssHtml'); - controller.send('newCustomization', object); - } - } - -}); diff --git a/app/assets/javascripts/discourse/initializers/live-development.js.es6 b/app/assets/javascripts/discourse/initializers/live-development.js.es6 index f73172e154..4fc17306cc 100644 --- a/app/assets/javascripts/discourse/initializers/live-development.js.es6 +++ b/app/assets/javascripts/discourse/initializers/live-development.js.es6 @@ -1,4 +1,45 @@ import DiscourseURL from 'discourse/lib/url'; +import { currentThemeKey } from 'discourse/lib/theme-selector'; + +export function refreshCSS(node, hash, newHref, options) { + + let $orig = $(node); + + if ($orig.data('reloading')) { + + if (options && options.force) { + clearTimeout($orig.data('timeout')); + $orig.data("copy").remove(); + } else { + return; + } + } + + if (!$orig.data('orig')) { + $orig.data('orig', node.href); + } + + $orig.data('reloading', true); + + const orig = $(node).data('orig'); + + let reloaded = $orig.clone(true); + if (hash) { + reloaded[0].href = orig + (orig.indexOf('?') >= 0 ? "&hash=" : "?hash=") + hash; + } else { + reloaded[0].href = newHref; + } + + $orig.after(reloaded); + + let timeout = setTimeout(()=>{ + $orig.remove(); + reloaded.data('reloading', false); + }, 2000); + + $orig.data("timeout", timeout); + $orig.data("copy", reloaded); +} // Use the message bus for live reloading of components for faster development. export default { @@ -47,18 +88,17 @@ export default { // Refresh if necessary document.location.reload(true); } else { + let themeKey = currentThemeKey(); + $('link').each(function() { - // TODO: stop bundling css in DEV please - if (true || (this.href.match(me.name) && me.hash)) { - if (!$(this).data('orig')) { - $(this).data('orig', this.href); + if (me.hasOwnProperty('theme_key') && me.new_href) { + let target = $(this).data('target'); + if (me.theme_key === themeKey && target === me.target) { + refreshCSS(this, null, me.new_href); } - const orig = $(this).data('orig'); - if (!me.hash) { - window.__uniq = window.__uniq || 1; - me.hash = window.__uniq++; - } - this.href = orig + (orig.indexOf('?') >= 0 ? "&hash=" : "?hash=") + me.hash; + } + else if (this.href.match(me.name) && (me.hash || me.new_href)) { + refreshCSS(this, me.hash, me.new_href); } }); } diff --git a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 index 15372b27ad..8a598edbca 100644 --- a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 +++ b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 @@ -196,19 +196,19 @@ export default { }, setTrackingToMuted(event) { - this.appEvents.trigger('topic-notifications-button:keyboard-trigger', {type: 'notification', id: 0, event}); + this.appEvents.trigger('topic-notifications-button:changed', {type: 'notification', id: 0, event}); }, setTrackingToRegular(event) { - this.appEvents.trigger('topic-notifications-button:keyboard-trigger', {type: 'notification', id: 1, event}); + this.appEvents.trigger('topic-notifications-button:changed', {type: 'notification', id: 1, event}); }, setTrackingToTracking(event) { - this.appEvents.trigger('topic-notifications-button:keyboard-trigger', {type: 'notification', id: 2, event}); + this.appEvents.trigger('topic-notifications-button:changed', {type: 'notification', id: 2, event}); }, setTrackingToWatching(event) { - this.appEvents.trigger('topic-notifications-button:keyboard-trigger', {type: 'notification', id: 3, event}); + this.appEvents.trigger('topic-notifications-button:changed', {type: 'notification', id: 3, event}); }, sendToTopicListItemView(action) { diff --git a/app/assets/javascripts/discourse/lib/lightbox.js.es6 b/app/assets/javascripts/discourse/lib/lightbox.js.es6 index 4f6f0d8451..c0f4f2f6f0 100644 --- a/app/assets/javascripts/discourse/lib/lightbox.js.es6 +++ b/app/assets/javascripts/discourse/lib/lightbox.js.es6 @@ -2,47 +2,47 @@ import loadScript from 'discourse/lib/load-script'; import { escapeExpression } from 'discourse/lib/utilities'; export default function($elem) { - $("a.lightbox", $elem).each(function(i, e) { - loadScript("/javascripts/jquery.magnific-popup-min.js").then(function() { - const $e = $(e); - // do not lightbox spoiled images - if ($e.parents(".spoiler").length > 0 || $e.parents(".spoiled").length > 0) { return; } + if (!$elem) { return; } + loadScript("/javascripts/jquery.magnific-popup.min.js").then(function() { + const spoilers = $elem.find('.spoiler a.lightbox, .spoiled a.lightbox'); + $elem.find('a.lightbox').not(spoilers).magnificPopup({ + type: "image", + closeOnContentClick: false, + removalDelay: 300, + mainClass: "mfp-zoom-in", - $e.magnificPopup({ - type: "image", - closeOnContentClick: false, - removalDelay: 300, - mainClass: "mfp-zoom-in", + gallery: { + enabled: true + }, - callbacks: { - open() { - const wrap = this.wrap, - img = this.currItem.img, - maxHeight = img.css("max-height"); + callbacks: { + open() { + const wrap = this.wrap, + img = this.currItem.img, + maxHeight = img.css("max-height"); - wrap.on("click.pinhandler", "img", function() { - wrap.toggleClass("mfp-force-scrollbars"); - img.css("max-height", wrap.hasClass("mfp-force-scrollbars") ? "none" : maxHeight); - }); - }, - beforeClose() { - this.wrap.off("click.pinhandler"); - this.wrap.removeClass("mfp-force-scrollbars"); - } + wrap.on("click.pinhandler", "img", function() { + wrap.toggleClass("mfp-force-scrollbars"); + img.css("max-height", wrap.hasClass("mfp-force-scrollbars") ? "none" : maxHeight); + }); }, - - image: { - titleSrc(item) { - const href = item.el.data("download-href") || item.src; - let src = [escapeExpression(item.el.attr("title")), $("span.informations", item.el).text().replace('x', '×')]; - if (!Discourse.SiteSettings.prevent_anons_from_downloading_files || Discourse.User.current()) { - src.push('' + I18n.t("lightbox.download") + ''); - } - return src.join(' · '); - } + beforeClose() { + this.wrap.off("click.pinhandler"); + this.wrap.removeClass("mfp-force-scrollbars"); } + }, + + image: { + titleSrc(item) { + const href = item.el.data("download-href") || item.src; + let src = [escapeExpression(item.el.attr("title")), $("span.informations", item.el).text().replace('x', '×')]; + if (!Discourse.SiteSettings.prevent_anons_from_downloading_files || Discourse.User.current()) { + src.push('' + I18n.t("lightbox.download") + ''); + } + return src.join(' · '); + } + } - }); }); }); } diff --git a/app/assets/javascripts/discourse/lib/load-script.js.es6 b/app/assets/javascripts/discourse/lib/load-script.js.es6 index cc24df86de..8df26b8bb6 100644 --- a/app/assets/javascripts/discourse/lib/load-script.js.es6 +++ b/app/assets/javascripts/discourse/lib/load-script.js.es6 @@ -24,6 +24,10 @@ function loadWithTag(path, cb) { }; } +export function loadCSS(url) { + return loadScript(url, { css: true }); +} + export default function loadScript(url, opts) { // TODO: Remove this once plugins have been updated not to use it: @@ -47,8 +51,11 @@ export default function loadScript(url, opts) { delete _loading[url]; }); - const cb = function() { + const cb = function(data) { _loaded[url] = true; + if (opts && opts.css) { + $("head").append(""); + } done(); resolve(); }; @@ -56,7 +63,8 @@ export default function loadScript(url, opts) { var cdnUrl = url; // Scripts should always load from CDN - if (Discourse.CDN && url[0] === "/" && url[1] !== "/") { + // CSS is type text, to accept it from a CDN we would need to handle CORS + if (!opts.css && Discourse.CDN && url[0] === "/" && url[1] !== "/") { cdnUrl = Discourse.CDN.replace(/\/$/,"") + url; } @@ -66,7 +74,7 @@ export default function loadScript(url, opts) { if (opts.scriptTag) { loadWithTag(cdnUrl, cb); } else { - ajax({url: cdnUrl, dataType: "script", cache: true}).then(cb); + ajax({url: cdnUrl, dataType: opts.css ? "text": "script", cache: true}).then(cb); } }); } diff --git a/app/assets/javascripts/discourse/lib/offset-calculator.js.es6 b/app/assets/javascripts/discourse/lib/offset-calculator.js.es6 index 92dd68dea5..8903f0ee17 100644 --- a/app/assets/javascripts/discourse/lib/offset-calculator.js.es6 +++ b/app/assets/javascripts/discourse/lib/offset-calculator.js.es6 @@ -33,10 +33,10 @@ export default function offsetCalculator(y) { } - if (inter > ideal) { const bottom = $('#topic-bottom').offset().top; const switchPos = bottom - rawWinHeight; + if (scrollTop > switchPos) { const p = Math.max(Math.min((scrollTop + inter - switchPos) / rawWinHeight, 1.0), 0.0); return ((1 - p) * ideal) + (p * inter); diff --git a/app/assets/javascripts/discourse/lib/render-tags.js.es6 b/app/assets/javascripts/discourse/lib/render-tags.js.es6 index 1389502f77..6989eab57b 100644 --- a/app/assets/javascripts/discourse/lib/render-tags.js.es6 +++ b/app/assets/javascripts/discourse/lib/render-tags.js.es6 @@ -43,7 +43,7 @@ export default function(topic, params){ buffer = "
    "; if (tags) { for(let i=0; i { + let elem = _.first($(keySelector)); + if (elem) { + elem.content = key; + } + + results.themes.forEach(theme => { + let node = $(`link[rel=stylesheet][data-target=${theme.target}]`)[0]; + if (node) { + refreshCSS(node, null, theme.url, {force: true}); + } + }); + }); + } +} + +export function listThemes(site) { + let themes = site.get('user_themes'); + + if (!themes) { + return null; + } + + let hasDefault = !!themes.findBy('default', true); + + let results = []; + if (!hasDefault) { + results.push({name: I18n.t('themes.default_description'), id: null}); + } + + themes.forEach(t=>{ + results.push({name: t.name, id: t.theme_key}); + }); + + return results.length === 0 ? null : results; +} diff --git a/app/assets/javascripts/discourse/mapping-router.js.es6 b/app/assets/javascripts/discourse/mapping-router.js.es6 index 6d30be749c..140ea94610 100644 --- a/app/assets/javascripts/discourse/mapping-router.js.es6 +++ b/app/assets/javascripts/discourse/mapping-router.js.es6 @@ -72,21 +72,10 @@ class RouteNode { if (this.name === 'root') { children.forEach(c => c.mapRoutes(router)); } else { - const builder = (children.length === 0) ? undefined : function() { children.forEach(c => c.mapRoutes(this)); }; router.route(this.name, this.opts, builder); - - // We can have multiple paths to the same route - const paths = Object.keys(this.paths); - if (paths.length > 1) { - paths.filter(p => p !== this.opts.path).forEach(path => { - const newOpts = jQuery.extend({}, this.opts, { path }); - console.log(`warning: we can't have duplicate route names anymore`, newOpts); - // router.route(this.name, newOpts, builder); - }); - } } } diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6 index 68ec25cfdd..197f861bd2 100644 --- a/app/assets/javascripts/discourse/models/composer.js.es6 +++ b/app/assets/javascripts/discourse/models/composer.js.es6 @@ -73,6 +73,11 @@ const Composer = RestModel.extend({ } }, + @computed('categoryId') + category(categoryId) { + return categoryId ? this.site.categories.findBy('id', categoryId) : null; + }, + creatingTopic: Em.computed.equal('action', CREATE_TOPIC), creatingPrivateMessage: Em.computed.equal('action', PRIVATE_MESSAGE), notCreatingPrivateMessage: Em.computed.not('creatingPrivateMessage'), @@ -279,13 +284,14 @@ const Composer = RestModel.extend({ @property minimumTitleLength **/ - minimumTitleLength: function() { - if (this.get('privateMessage')) { + @computed('privateMessage') + minimumTitleLength(privateMessage) { + if (privateMessage) { return this.siteSettings.min_private_message_title_length; } else { return this.siteSettings.min_topic_title_length; } - }.property('privateMessage'), + }, @computed('minimumPostLength', 'replyLength', 'canEditTopicFeaturedLink') missingReplyCharacters(minimumPostLength, replyLength, canEditTopicFeaturedLink) { @@ -299,16 +305,19 @@ const Composer = RestModel.extend({ @property minimumPostLength **/ - minimumPostLength: function() { - if( this.get('privateMessage') ) { + @computed('privateMessage', 'topicFirstPost', 'topic.pm_with_non_human_user') + minimumPostLength(privateMessage, topicFirstPost, pmWithNonHumanUser) { + if (pmWithNonHumanUser) { + return 1; + } else if (privateMessage) { return this.siteSettings.min_private_message_post_length; - } else if (this.get('topicFirstPost')) { + } else if (topicFirstPost) { // first post (topic body) return this.siteSettings.min_first_post_length; } else { return this.siteSettings.min_post_length; } - }.property('privateMessage', 'topicFirstPost'), + }, /** Computes the length of the title minus non-significant whitespaces diff --git a/app/assets/javascripts/discourse/models/group.js.es6 b/app/assets/javascripts/discourse/models/group.js.es6 index 7d1ab55e70..2af528f818 100644 --- a/app/assets/javascripts/discourse/models/group.js.es6 +++ b/app/assets/javascripts/discourse/models/group.js.es6 @@ -139,7 +139,8 @@ const Group = RestModel.extend({ bio_raw: this.get('bio_raw'), public: this.get('public'), allow_membership_requests: this.get('allow_membership_requests'), - full_name: this.get('full_name') + full_name: this.get('full_name'), + default_notification_level: this.get('default_notification_level') }; }, @@ -191,10 +192,10 @@ const Group = RestModel.extend({ }); }, - setNotification(notification_level) { + setNotification(notification_level, userId) { this.set("group_user.notification_level", notification_level); return ajax(`/groups/${this.get("name")}/notifications`, { - data: { notification_level }, + data: { notification_level, user_id: userId }, type: "POST" }); } diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6 index 753f2dd5ab..0d1ef7cdac 100644 --- a/app/assets/javascripts/discourse/models/store.js.es6 +++ b/app/assets/javascripts/discourse/models/store.js.es6 @@ -56,9 +56,13 @@ export default Ember.Object.extend({ }, findAll(type, findArgs) { - const self = this; - return this.adapterFor(type).findAll(this, type, findArgs).then(function(result) { - return self._resultSet(type, result); + const adapter = this.adapterFor(type); + return adapter.findAll(this, type, findArgs).then((result) => { + let results = this._resultSet(type, result); + if (adapter.afterFindAll) { + results = adapter.afterFindAll(results); + } + return results; }); }, diff --git a/app/assets/javascripts/discourse/routes/full-page-search.js.es6 b/app/assets/javascripts/discourse/routes/full-page-search.js.es6 index eb0cc01092..41ea9d4829 100644 --- a/app/assets/javascripts/discourse/routes/full-page-search.js.es6 +++ b/app/assets/javascripts/discourse/routes/full-page-search.js.es6 @@ -8,6 +8,10 @@ import { getOwner } from 'discourse-common/lib/get-owner'; export default Discourse.Route.extend({ queryParams: { q: {}, expanded: false, context_id: {}, context: {}, skip_context: {} }, + titleToken() { + return I18n.t('search.results_page'); + }, + model(params) { const cached = getTransient('lastSearch'); var args = { q: params.q }; diff --git a/app/assets/javascripts/discourse/routes/preferences.js.es6 b/app/assets/javascripts/discourse/routes/preferences.js.es6 index b26648042c..3ff42ca52e 100644 --- a/app/assets/javascripts/discourse/routes/preferences.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences.js.es6 @@ -1,6 +1,7 @@ import RestrictedUserRoute from "discourse/routes/restricted-user"; import showModal from 'discourse/lib/show-modal'; import { popupAjaxError } from 'discourse/lib/ajax-error'; +import { currentThemeKey } from 'discourse/lib/theme-selector'; export default RestrictedUserRoute.extend({ model() { @@ -11,7 +12,8 @@ export default RestrictedUserRoute.extend({ controller.reset(); controller.setProperties({ model: user, - newNameInput: user.get('name') + newNameInput: user.get('name'), + selectedTheme: $.cookie('theme_key') || currentThemeKey() }); }, diff --git a/app/assets/javascripts/discourse/routes/topic.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6 index 3fcb115fbf..616ca7e000 100644 --- a/app/assets/javascripts/discourse/routes/topic.js.es6 +++ b/app/assets/javascripts/discourse/routes/topic.js.es6 @@ -19,7 +19,7 @@ const TopicRoute = Discourse.Route.extend({ titleToken() { const model = this.modelFor('topic'); if (model) { - const result = model.get('title'), + const result = model.get('unicode_title') ? model.get('unicode_title') : model.get('title'), cat = model.get('category'); // Only display uncategorized in the title tag if it was renamed @@ -54,7 +54,7 @@ const TopicRoute = Discourse.Route.extend({ const model = this.modelFor('topic'); model.set('topic_status_update', Ember.Object.create(model.get('topic_status_update'))); showModal('edit-topic-status-update', { model }); - this.controllerFor('modal').set('modalClass', 'topic-close-modal'); + this.controllerFor('modal').set('modalClass', 'edit-topic-status-update-modal'); }, showChangeTimestamp() { diff --git a/app/assets/javascripts/discourse/templates/badges/index.hbs b/app/assets/javascripts/discourse/templates/badges/index.hbs index b30725693b..08b7c79b5e 100644 --- a/app/assets/javascripts/discourse/templates/badges/index.hbs +++ b/app/assets/javascripts/discourse/templates/badges/index.hbs @@ -10,7 +10,7 @@
    {{#each bg.badges as |b|}} - {{badge-card badge=b navigateOnClick="true"}} + {{badge-card badge=b}} {{/each}}

    {{/each}} diff --git a/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs b/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs index 455f17eea3..65267c23c5 100644 --- a/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs +++ b/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs @@ -1,24 +1,52 @@
    - + - {{#if inputExamplesKey}} -
    - {{i18n inputExamplesKey}} -
    - {{/if}} + {{auto-update-input-selector + valueAttribute="id" + minimumResultsForSearch=-1 + statusType=statusType + value=selection + input=input + width="50%" + none="topic.auto_update_input.none"}}
    - {{#unless hideBasedOnLastPost}} + {{#if isCustom}} +
    + {{fa-icon "calendar"}} {{date-picker-future value=date defaultDate=date}} +
    + +
    + {{fa-icon "clock-o"}} + {{input type="time" value=time}} +
    + {{/if}} + + {{#if isBasedOnLastPost}}
    - {{/unless}} + + {{#if willCloseImmediately}} +
    + {{fa-icon "warning"}} + {{willCloseI18n}} +
    + {{/if}} + {{/if}} + + {{#if showTopicStatusInfo}} +
    + {{topic-status-info + statusType=statusType + executeAt=executeAt + basedOnLastPost=basedOnLastPost + duration=duration + categoryId=categoryId}} +
    + {{/if}}
    diff --git a/app/assets/javascripts/discourse/templates/components/badge-card.hbs b/app/assets/javascripts/discourse/templates/components/badge-card.hbs index 765322dec8..2a41b36605 100644 --- a/app/assets/javascripts/discourse/templates/components/badge-card.hbs +++ b/app/assets/javascripts/discourse/templates/components/badge-card.hbs @@ -10,7 +10,7 @@
    -

    {{badge.name}}

    +

    {{badge.name}}

    {{{summary}}}
    diff --git a/app/assets/javascripts/discourse/templates/components/color-input.hbs b/app/assets/javascripts/discourse/templates/components/color-input.hbs index e69d19d7e1..1b9f36ecb3 100644 --- a/app/assets/javascripts/discourse/templates/components/color-input.hbs +++ b/app/assets/javascripts/discourse/templates/components/color-input.hbs @@ -1 +1 @@ -{{text-field class="hex-input" value=hexValue maxlength="6"}} +{{text-field class="hex-input" value=hexValue maxlength="6"}} diff --git a/app/assets/javascripts/discourse/templates/components/composer-user-selector.hbs b/app/assets/javascripts/discourse/templates/components/composer-user-selector.hbs index 3e5d25059e..402416a1d6 100644 --- a/app/assets/javascripts/discourse/templates/components/composer-user-selector.hbs +++ b/app/assets/javascripts/discourse/templates/components/composer-user-selector.hbs @@ -7,7 +7,8 @@ placeholderKey="composer.users_placeholder" tabindex="1" usernames=usernames - hasGroups=hasGroups}} + hasGroups=hasGroups + autocomplete="off"}} {{else}}
    {{limitedUsernames}} diff --git a/app/assets/javascripts/discourse/templates/components/date-picker.hbs b/app/assets/javascripts/discourse/templates/components/date-picker.hbs index a2c89401ab..d49379d954 100644 --- a/app/assets/javascripts/discourse/templates/components/date-picker.hbs +++ b/app/assets/javascripts/discourse/templates/components/date-picker.hbs @@ -1 +1 @@ - +{{input type="text" class="date-picker" placeholder=placeholder}} diff --git a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs index e1fec09080..ddfb1ce11b 100644 --- a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs @@ -1,10 +1,15 @@
    - {{auto-update-input - inputLabelKey='topic.auto_close.label' - input=category.auto_close_hours - basedOnLastPost=category.auto_close_based_on_last_post - inputExamplesKey='' - limited=true}} +
    + + + +
    diff --git a/app/assets/javascripts/discourse/templates/components/featured-topic.hbs b/app/assets/javascripts/discourse/templates/components/featured-topic.hbs index e84b8bf4e7..7061778b8e 100644 --- a/app/assets/javascripts/discourse/templates/components/featured-topic.hbs +++ b/app/assets/javascripts/discourse/templates/components/featured-topic.hbs @@ -1,4 +1,4 @@ -{{topic-status topic=topic}} +{{raw "topic-status" topic=topic}} {{{unbound topic.fancyTitle}}} {{topic-post-badges newPosts=topic.totalUnread unseen=topic.unseen url=topic.lastUnreadUrl}} diff --git a/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs b/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs deleted file mode 100644 index b34f6c4b9f..0000000000 --- a/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    - - {{d-button class="fileSelect" action="selectFile" class="" icon="upload" label="upload_selector.select_file"}} - {{conditional-loading-spinner condition=loading size="small"}} -
    -
    {{i18n "alternation"}}
    -
    - {{textarea value=value}} -
    -
    {{fa-icon "upload"}}
    -
    diff --git a/app/assets/javascripts/discourse/templates/components/mobile-category-topic.hbs b/app/assets/javascripts/discourse/templates/components/mobile-category-topic.hbs index c3827841dc..6cc45d2f1e 100644 --- a/app/assets/javascripts/discourse/templates/components/mobile-category-topic.hbs +++ b/app/assets/javascripts/discourse/templates/components/mobile-category-topic.hbs @@ -1,6 +1,6 @@
    - {{topic-status topic=topic}} + {{raw "topic-status" topic=topic}} {{topic-link topic}} {{#if topic.unseen}} diff --git a/app/assets/javascripts/discourse/templates/modal/change-owner.hbs b/app/assets/javascripts/discourse/templates/modal/change-owner.hbs index c23cefd469..33cbe1dd24 100644 --- a/app/assets/javascripts/discourse/templates/modal/change-owner.hbs +++ b/app/assets/javascripts/discourse/templates/modal/change-owner.hbs @@ -5,8 +5,11 @@

    - - {{user-selector single="true" usernames=new_user placeholderKey="topic.change_owner.placeholder"}} + + {{user-selector single="true" + usernames=new_user + placeholderKey="topic.change_owner.placeholder" + autocomplete="off"}}
    {{/d-modal-body}} diff --git a/app/assets/javascripts/discourse/templates/modal/edit-topic-status-update.hbs b/app/assets/javascripts/discourse/templates/modal/edit-topic-status-update.hbs index 8fca98f413..c1039fd9b4 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit-topic-status-update.hbs +++ b/app/assets/javascripts/discourse/templates/modal/edit-topic-status-update.hbs @@ -1,59 +1,14 @@
    {{#d-modal-body title="topic.topic_status_update.title" autoFocus="false"}} -
    - {{radio-button - disabled=disableAutoClose - name="auto-close" - id="auto-close" - value=closeStatusType - selection=selection}} - - - - {{radio-button - disabled=disableAutoOpen - name="auto-reopen" - id="auto-reopen" - value=openStatusType - selection=selection}} - - - - {{radio-button - disabled=disablePublishToCategory - name="publish-to-category" - id="publish-to-category" - value=publishToCategoryStatusType - selection=selection}} - - +
    + {{combo-box content=statusUpdates value=selection width="50%"}}
    {{#if autoOpen}} {{auto-update-input - inputLabelKey='topic.topic_status_update.time' input=updateTime - inputValid=updateTimeValid - hideBasedOnLastPost=true + statusType=selection basedOnLastPost=false}} {{else if publishToCategory}}
    @@ -62,25 +17,16 @@
    {{auto-update-input - inputLabelKey='topic.topic_status_update.time' input=updateTime - inputValid=updateTimeValid - hideBasedOnLastPost=true + statusType=selection + categoryId=categoryId basedOnLastPost=false}} {{else if autoClose}} {{auto-update-input - inputLabelKey='topic.topic_status_update.time' input=updateTime - inputValid=updateTimeValid - limited=topicStatusUpdate.based_on_last_post - basedOnLastPost=topicStatusUpdate.based_on_last_post}} - - {{#if willCloseImmediately}} -
    - {{fa-icon "warning"}} - {{willCloseI18n}} -
    - {{/if}} + statusType=selection + basedOnLastPost=topicStatusUpdate.based_on_last_post + lastPostedAt=model.last_posted_at}} {{/if}}
    {{/d-modal-body}} diff --git a/app/assets/javascripts/discourse/templates/preferences.hbs b/app/assets/javascripts/discourse/templates/preferences.hbs index 6fea4f7f6f..3a0ec94d69 100644 --- a/app/assets/javascripts/discourse/templates/preferences.hbs +++ b/app/assets/javascripts/discourse/templates/preferences.hbs @@ -354,6 +354,15 @@
    {{/if}} + {{#if userSelectableThemes}} +
    + +
    + {{combo-box content=userSelectableThemes value=selectedTheme}} +
    +
    + {{/if}} + {{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
    diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index c22ed994bb..4c167b8e39 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -174,7 +174,13 @@ {{#conditional-loading-spinner condition=model.postStream.loadingFilter}} {{#if loadedAllPosts}} - {{topic-status-info topic=model}} + {{topic-status-info + statusType=model.topic_status_update.status_type + executeAt=model.topic_status_update.execute_at + basedOnLastPost=model.topic_status_update.based_on_last_post + duration=model.topic_status_update.duration + categoryId=model.topic_status_update.category_id}} + {{#if session.showSignupCta}} {{! replace "Log In to Reply" with the infobox }} {{signup-cta}} diff --git a/app/assets/javascripts/discourse/templates/user.hbs b/app/assets/javascripts/discourse/templates/user.hbs index 13f1c6b222..7970c3f9a0 100644 --- a/app/assets/javascripts/discourse/templates/user.hbs +++ b/app/assets/javascripts/discourse/templates/user.hbs @@ -24,8 +24,8 @@ {{#if model.number_of_suspensions}}
    {{model.number_of_suspensions}} {{i18n 'user.staff_counters.suspensions'}}
    {{/if}} - {{#if model.number_of_warnings}} -
    {{model.number_of_warnings}} {{i18n 'user.staff_counters.warnings_received'}}
    + {{#if model.warnings_received_count}} +
    {{model.warnings_received_count}} {{i18n 'user.staff_counters.warnings_received'}}
    {{/if}}
    {{/unless}} diff --git a/app/assets/javascripts/discourse/templates/user/badges.hbs b/app/assets/javascripts/discourse/templates/user/badges.hbs index 860af54a92..03210567ce 100644 --- a/app/assets/javascripts/discourse/templates/user/badges.hbs +++ b/app/assets/javascripts/discourse/templates/user/badges.hbs @@ -1,5 +1,5 @@ {{#d-section pageClass="user-badges" class="user-content user-badges-list"}} {{#each sortedBadges as |ub|}} - {{badge-card badge=ub.badge count=ub.count navigateOnClick="true" username=username}} + {{badge-card badge=ub.badge count=ub.count username=username}} {{/each}} {{/d-section}} diff --git a/app/assets/javascripts/discourse/templates/user/messages.hbs b/app/assets/javascripts/discourse/templates/user/messages.hbs index d9be88452e..ece0c407b7 100644 --- a/app/assets/javascripts/discourse/templates/user/messages.hbs +++ b/app/assets/javascripts/discourse/templates/user/messages.hbs @@ -73,7 +73,7 @@ {{/if}} {{#if isGroup}} - {{group-notifications-button group=group}} + {{group-notifications-button group=group user=model}} {{/if}}
    diff --git a/app/assets/javascripts/discourse/templates/user/summary.hbs b/app/assets/javascripts/discourse/templates/user/summary.hbs index 5dfd55a16a..d3444c62b1 100644 --- a/app/assets/javascripts/discourse/templates/user/summary.hbs +++ b/app/assets/javascripts/discourse/templates/user/summary.hbs @@ -179,7 +179,7 @@

    {{i18n "user.summary.top_badges"}}

    {{#each model.badges as |badge|}} - {{badge-card badge=badge count=badge.count navigateOnClick="true" username=user.username_lower}} + {{badge-card badge=badge count=badge.count username=user.username_lower}} {{else}}

    {{i18n "user.summary.no_badges"}}

    {{/each}} diff --git a/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 b/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 index 3e9979cad1..890a7a7b4a 100644 --- a/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 +++ b/app/assets/javascripts/discourse/widgets/hamburger-menu.js.es6 @@ -163,22 +163,22 @@ export default createWidget('hamburger-menu', { const prioritizeFaq = this.currentUser && !this.currentUser.read_faq; if (prioritizeFaq) { - results.push(this.attach('menu-links', { heading: true, contents: () => { + results.push(this.attach('menu-links', { name: 'faq-link', heading: true, contents: () => { return this.attach('priority-faq-link', { href: faqUrl }); }})); } if (currentUser && currentUser.staff) { - results.push(this.attach('menu-links', { contents: () => { + results.push(this.attach('menu-links', { name: 'admin-links', contents: () => { const extraLinks = flatten(applyDecorators(this, 'admin-links', this.attrs, this.state)); return this.adminLinks().concat(extraLinks); }})); } - results.push(this.attach('menu-links', { contents: () => this.generalLinks() })); + results.push(this.attach('menu-links', {name: 'general-links', contents: () => this.generalLinks() })); results.push(this.listCategories()); results.push(h('hr')); - results.push(this.attach('menu-links', { omitRule: true, contents: () => this.footerLinks(prioritizeFaq, faqUrl) })); + results.push(this.attach('menu-links', {name: 'footer-links', omitRule: true, contents: () => this.footerLinks(prioritizeFaq, faqUrl) })); return results; }, diff --git a/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 b/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 index 3879e3739a..38784a3941 100644 --- a/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 @@ -136,6 +136,10 @@ export default class PostCooked { div.html(result.cooked); div.highlight(originalText, {caseSensitive: true, element: 'span', className: 'highlighted'}); $blockQuote.showHtml(div, 'fast', finished); + }).catch((e) => { + if (e.jqXHR.status === 404) { + $blockQuote.showHtml($("
    "), 'fast', finished); + } }); } else { // Hide expanded quote diff --git a/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 index 9c9f0cec7c..4bf84e5631 100644 --- a/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 @@ -88,7 +88,7 @@ export default createWidget('topic-notifications-button', { return this.attrs.topic.get('details').updateNotifications(id); }, - topicNotificationsButtonKeyboardTrigger(msg) { + topicNotificationsButtonChanged(msg) { switch(msg.type) { case 'notification': this.notificationLevelChanged(msg.id); diff --git a/app/assets/javascripts/wizard/components/theme-preview.js.es6 b/app/assets/javascripts/wizard/components/theme-preview.js.es6 index 19a9863b15..d4ec95b217 100644 --- a/app/assets/javascripts/wizard/components/theme-preview.js.es6 +++ b/app/assets/javascripts/wizard/components/theme-preview.js.es6 @@ -11,7 +11,7 @@ export default createPreviewComponent(659, 320, { logo: null, avatar: null, - @observes('step.fieldsById.theme_id.value') + @observes('step.fieldsById.base_scheme_id.value') themeChanged() { this.triggerRepaint(); }, diff --git a/app/assets/javascripts/wizard/models/wizard.js.es6 b/app/assets/javascripts/wizard/models/wizard.js.es6 index d98ba5e51e..8960354e58 100644 --- a/app/assets/javascripts/wizard/models/wizard.js.es6 +++ b/app/assets/javascripts/wizard/models/wizard.js.es6 @@ -25,7 +25,7 @@ const Wizard = Ember.Object.extend({ const colorStep = this.get('steps').findBy('id', 'colors'); if (!colorStep) { return; } - const themeChoice = colorStep.get('fieldsById.theme_id'); + const themeChoice = colorStep.get('fieldsById.base_scheme_id'); if (!themeChoice) { return; } const themeId = themeChoice.get('value'); diff --git a/app/assets/javascripts/wizard/templates/components/wizard-field.hbs b/app/assets/javascripts/wizard/templates/components/wizard-field.hbs index 37c216baae..8f804940fb 100644 --- a/app/assets/javascripts/wizard/templates/components/wizard-field.hbs +++ b/app/assets/javascripts/wizard/templates/components/wizard-field.hbs @@ -11,5 +11,5 @@
    {{#if field.errorDescription}} -
    {{field.errorDescription}}
    +
    {{{field.errorDescription}}}
    {{/if}} diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 0fba7c199b..93878dadb6 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -3,6 +3,8 @@ @import "common/foundation/mixins"; @import "common/foundation/helpers"; +@import "common/admin/customize"; + $mobile-breakpoint: 700px; // Change the box model for .admin-content @@ -551,6 +553,9 @@ section.details { .display-row.associations .value { width: 750px; + @media (max-width: $mobile-breakpoint) { + width: 100%; + } } .display-row { @@ -596,6 +601,9 @@ section.details { width: 480px; float: left; margin-left: 12px; + @media (max-width: $mobile-breakpoint) { + width: 100%; + } .btn { margin-right: 5px; } @@ -724,138 +732,6 @@ section.details { } } -// Customise area -.customize { - .admin-footer { - margin-top: 20px; - } - .current-style.maximized { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 100000; - background-color: white; - width: 100%; - padding: 0; - margin: 0; - .wrapper { - position: absolute; - top: 20px; - bottom: 10px; - left: 20px; - right: 20px; - } - } - .nav.nav-pills { - margin-left: 10px; - } - .content-list, .current-style { - float: left; - } - .content-list ul { - margin-bottom: 10px; - } - .current-style { - .nav.nav-pills{ - position: relative; - } - .toggle-mobile { - position: absolute; - right: 35px; - font-size: 20px; - } - .toggle-maximize { - position: absolute; - right: -5px; - } - .delete-link { - margin-left: 15px; - margin-top: 5px; - } - .preview-link { - margin-left: 15px; - } - .export { - float: right; - } - padding-left: 10px; - width: 70%; - .style-name { - width: 350px; - height: 25px; - // Remove height to for `box-sizing: border-box` - height: auto; - } - .ace-wrapper { - position: relative; - height: 400px; - width: 100%; - } - &.maximized { - .admin-container { - position: absolute; - bottom: 50px; - top: 80px; - width: 100%; - } - .admin-footer { - position: absolute; - bottom: 10px; - } - .ace-wrapper { - height: 100%; - } - } - .ace_editor { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - } - .status-actions { - float: right; - margin-top: 7px; - span { - margin-right: 10px; - } - } - .buttons { - float: left; - width: 200px; - .saving { - padding: 5px 0 0 0; - margin-left: 10px; - width: 80px; - color: $primary; - } - } - } - .color-scheme { - .controls { - span, button, a { - margin-right: 10px; - } - } - } - .colors { - thead th { border: none; } - td.hex { width: 100px; } - td.actions { width: 200px; } - .hex-input { width: 80px; margin-bottom: 0; } - .hex { text-align: center; } - .description { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } - - .invalid .hex input { - background-color: white; - color: black; - border-color: $danger; - } - } -} - .admin-flags { .hidden-post td.excerpt, .hidden-post td.user { @@ -1949,7 +1825,7 @@ table#user-badges { // Mobile specific style for Admin IP Lookup box .mobile-view .admin-contents .ip-lookup .location-box { width: 300px; - left: 20px; + left: -100%; } .cboxcontainer { @@ -1970,6 +1846,12 @@ table#user-badges { background: $secondary; } } + +.inline-edit label { + display: inline-block; + margin-right: 20px; +} + .cbox0 { background: blend-primary-secondary(0%); } .cbox10 { background: blend-primary-secondary(10%); } .cbox20 { background: blend-primary-secondary(20%); } diff --git a/app/assets/stylesheets/common/admin/customize.scss b/app/assets/stylesheets/common/admin/customize.scss new file mode 100644 index 0000000000..e03ecb6b60 --- /dev/null +++ b/app/assets/stylesheets/common/admin/customize.scss @@ -0,0 +1,191 @@ +// Customise area +.customize { + h1 { + margin-bottom: 10px; + input { + margin-bottom: 0; + } + } + + .field-error { + margin-top: 10px; + margin-bottom: 10px; + background-color: dark-light-diff($quaternary, $secondary, 70%, -70%); + padding: 5px; + } + + .edit-main-nav { + .nav-pills:after, .nav-pills:before { + display: inline; + content: ""; + } + .show-overidden { + float: right; + } + margin-bottom: 10px; + } + + .admin-container { + padding-left: 10px; + padding-right: 10px; + } + .admin-footer { + margin-top: 20px; + } + .select2-chosen, .color-schemes li { + .fa { + margin-right: 6px; + color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + } + } + .show-current-style { + margin-left: 20px; + float: left; + width: 70%; + h2 { + margin-bottom: 15px; + } + h3 { + margin-bottom: 10px; + margin-top: 30px; + } + } + + .current-style.maximized { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 100000; + background-color: white; + width: 100%; + padding: 0; + margin: 0; + .wrapper { + position: absolute; + top: 20px; + bottom: 10px; + left: 20px; + right: 20px; + } + } + + .nav.nav-pills.fields { + margin-left: 10px; + } + .content-list, .current-style { + float: left; + } + .content-list ul { + margin-bottom: 10px; + } + .current-style { + width: 100%; + + .admin-container { + margin: 0; + } + + .nav.target { + li { + position: relative; + } + margin-top: 15px; + .fa { + margin-left: 3px; + } + li.mobile a { + padding-right: 25px; + } + .fa-mobile { + position: absolute; + right: 10px; + top: 3px; + font-size: 1.5em; + } + } + + .toggle-maximize { + position: absolute; + right: -5px; + } + + .ace-wrapper { + position: relative; + height: 600px; + width: 100%; + } + + &.maximized { + .admin-container { + position: absolute; + bottom: 50px; + top: 80px; + width: 100%; + } + .admin-footer { + position: absolute; + bottom: 10px; + } + .ace-wrapper { + height: 100%; + } + } + + .ace_editor { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + } + + .status-actions { + float: right; + margin-top: 7px; + span { + margin-right: 10px; + } + } + + .buttons { + float: left; + width: 200px; + .saving { + padding: 5px 0 0 0; + margin-left: 10px; + width: 80px; + color: $primary; + } + } + } + .color-scheme { + .controls { + span, button, a { + margin-right: 10px; + } + } + } + .colors { + thead th { border: none; } + td.hex { width: 160px; } + td.actions { width: 200px; } + .hex-input { width: 80px; margin-bottom: 0; } + .hex { text-align: center; } + .description { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + + .invalid .hex input { + background-color: white; + color: black; + border-color: $danger; + } + } + + .status-message { + display: block; + font-size: 0.8em; + margin-top: 8px; + } +} + diff --git a/app/assets/stylesheets/common/base/category-list.scss b/app/assets/stylesheets/common/base/category-list.scss index 5d88a6155c..477448dcda 100644 --- a/app/assets/stylesheets/common/base/category-list.scss +++ b/app/assets/stylesheets/common/base/category-list.scss @@ -12,7 +12,10 @@ align-content: flex-start; box-sizing: border-box; - border-width: 0 0 0 6px; + + border-width: 0; + border-left-width: 6px; + border-style: solid; border-color: blend-primary-secondary(20%); @@ -34,7 +37,10 @@ .category-box-inner { width: 100%; padding: 0; - border-width: 2px 2px 2px 0; + + border-width: 2px; + border-left-width: 0; + border-style: solid; border-color: blend-primary-secondary(20%); } diff --git a/app/assets/stylesheets/common/base/edit-topic-status-update-modal.scss b/app/assets/stylesheets/common/base/edit-topic-status-update-modal.scss new file mode 100644 index 0000000000..65b06ed8f4 --- /dev/null +++ b/app/assets/stylesheets/common/base/edit-topic-status-update-modal.scss @@ -0,0 +1,42 @@ +.edit-topic-status-update-modal { + .modal-body { + max-height: none; + } + + input.date-picker, input[type="time"] { + width: 150px; + text-align: left; + } + + label { + display: inline-block; + } + + .btn.pull-right { + margin-right: 10px; + } + + .auto-update-input { + input { + margin: 0; + } + + .alert-info { + margin: 0 -15px -15px -15px; + } + + .pika-single { + position: relative !important; + } + + .topic-status-info { + border: none; + padding: 0; + + h3 { + font-weight: normal; + font-size: 15px; + } + } + } +} diff --git a/app/assets/stylesheets/common/base/tagging.scss b/app/assets/stylesheets/common/base/tagging.scss index 7fb5f4813e..f691175120 100644 --- a/app/assets/stylesheets/common/base/tagging.scss +++ b/app/assets/stylesheets/common/base/tagging.scss @@ -74,10 +74,18 @@ $tag-color: scale-color($primary, $lightness: 40%); color: $tag-color; } + .extra-info-wrapper & { + color: $header-primary !important; + } + &.box { background-color: scale-color($primary, $lightness: 90%); color: scale-color($primary, $lightness: 30%); padding: 2px 8px; + .extra-info-wrapper & { + background-color: scale-color($header-primary, $lightness: 90%); + color: scale-color($header-primary, $lightness: 30%); + } } &.simple, &.simple:visited, &.simple:hover { diff --git a/app/assets/stylesheets/common/base/topic-close-modal.scss b/app/assets/stylesheets/common/base/topic-close-modal.scss deleted file mode 100644 index 0a638b6f81..0000000000 --- a/app/assets/stylesheets/common/base/topic-close-modal.scss +++ /dev/null @@ -1,29 +0,0 @@ -.topic-close-modal { - label { - display: inline-block; - } - - .radios { - padding-bottom: 20px; - display: inline-block; - - input[type='radio'] { - vertical-align: middle; - margin: 0px; - } - - label { - padding: 0 10px 0px 5px; - } - } - - .btn.pull-right { - margin-right: 10px; - } - - .auto-update-input { - input { - margin: 0; - } - } -} diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss index 3c87530f5b..7c25c2652a 100644 --- a/app/assets/stylesheets/common/base/user-badges.scss +++ b/app/assets/stylesheets/common/base/user-badges.scss @@ -142,7 +142,7 @@ right: 5px; top: 5px; font-weight: bold; - color: dark-light-diff($primary, $secondary, 50%, -65%); + color: dark-light-diff($primary, $secondary, 50%, -15%); font-size: 1.2em; } diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index 6c7fbfbb51..911c461f15 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -1,4 +1,3 @@ - // styling of bottom section .user-stream .child-actions { margin-top: 8px; diff --git a/app/assets/stylesheets/common/components/auto-update-input-selector.scss b/app/assets/stylesheets/common/components/auto-update-input-selector.scss new file mode 100644 index 0000000000..65207f3c1c --- /dev/null +++ b/app/assets/stylesheets/common/components/auto-update-input-selector.scss @@ -0,0 +1,9 @@ +.auto-update-input-selector-datetime { + float: right; + color: lighten($primary, 40%); + font-size: 13px; +} + +.auto-update-input-selector-icons { + margin-right: 10px; +} diff --git a/app/assets/stylesheets/common/components/badges.css.scss b/app/assets/stylesheets/common/components/badges.scss similarity index 100% rename from app/assets/stylesheets/common/components/badges.css.scss rename to app/assets/stylesheets/common/components/badges.scss diff --git a/app/assets/stylesheets/common/components/banner.css.scss b/app/assets/stylesheets/common/components/banner.scss similarity index 96% rename from app/assets/stylesheets/common/components/banner.css.scss rename to app/assets/stylesheets/common/components/banner.scss index 7faca616cb..5fe427df04 100644 --- a/app/assets/stylesheets/common/components/banner.css.scss +++ b/app/assets/stylesheets/common/components/banner.scss @@ -20,6 +20,7 @@ margin-top: -5px; color: scale-color($tertiary, $lightness: 70%); padding-left: 5px; + float: right; } .meta { diff --git a/app/assets/stylesheets/common/components/buttons.css.scss b/app/assets/stylesheets/common/components/buttons.scss similarity index 100% rename from app/assets/stylesheets/common/components/buttons.css.scss rename to app/assets/stylesheets/common/components/buttons.scss diff --git a/app/assets/stylesheets/common/components/date-picker.css.scss b/app/assets/stylesheets/common/components/date-picker.scss similarity index 100% rename from app/assets/stylesheets/common/components/date-picker.css.scss rename to app/assets/stylesheets/common/components/date-picker.scss diff --git a/app/assets/stylesheets/common/components/keyboard_shortcuts.css.scss b/app/assets/stylesheets/common/components/keyboard_shortcuts.scss similarity index 100% rename from app/assets/stylesheets/common/components/keyboard_shortcuts.css.scss rename to app/assets/stylesheets/common/components/keyboard_shortcuts.scss diff --git a/app/assets/stylesheets/common/components/navs.css.scss b/app/assets/stylesheets/common/components/navs.scss similarity index 100% rename from app/assets/stylesheets/common/components/navs.css.scss rename to app/assets/stylesheets/common/components/navs.scss diff --git a/app/assets/stylesheets/common/foundation/base.scss b/app/assets/stylesheets/common/foundation/base.scss index 2a3c5588d0..35b0418187 100644 --- a/app/assets/stylesheets/common/foundation/base.scss +++ b/app/assets/stylesheets/common/foundation/base.scss @@ -1,5 +1,5 @@ -@import "common/foundation/variables"; -@import "common/foundation/mixins"; +@import "./variables"; +@import "./mixins"; // -------------------------------------------------- // Base styles for HTML elements diff --git a/app/assets/stylesheets/common/foundation/variables.scss b/app/assets/stylesheets/common/foundation/variables.scss index b06099e3b5..43eb44cdb2 100644 --- a/app/assets/stylesheets/common/foundation/variables.scss +++ b/app/assets/stylesheets/common/foundation/variables.scss @@ -28,7 +28,7 @@ $base-font-size: 14px !default; $base-line-height: 19px !default; $base-font-family: Helvetica, Arial, sans-serif !default; -/* These files don't actually exist. They're injected by DiscourseSassImporter. */ +/* These files don't actually exist. They're injected by Stylesheet::Compiler. */ @import "theme_variables"; @import "plugins_variables"; @import "common/foundation/math"; diff --git a/app/assets/stylesheets/common/printer-friendly.scss b/app/assets/stylesheets/common/printer-friendly.scss index 6d62eb40d8..d640737b47 100644 --- a/app/assets/stylesheets/common/printer-friendly.scss +++ b/app/assets/stylesheets/common/printer-friendly.scss @@ -16,7 +16,7 @@ .show-topic-admin, #topic-progress, .quote-controls, - #topic-status-info, + .topic-status-info, div.lazyYT, .post-info.edits, .post-action, diff --git a/app/assets/stylesheets/desktop.scss b/app/assets/stylesheets/desktop.scss index 807b43c726..71f19278ed 100644 --- a/app/assets/stylesheets/desktop.scss +++ b/app/assets/stylesheets/desktop.scss @@ -21,7 +21,7 @@ @import "desktop/menu-panel"; @import "desktop/group"; -/* These files doesn't actually exist, they are injected by DiscourseSassImporter. */ +/* These files doesn't actually exist, they are injected by Stylesheet::Compiler. */ @import "plugins"; @import "plugins_desktop"; diff --git a/app/assets/stylesheets/desktop/topic.scss b/app/assets/stylesheets/desktop/topic.scss index 59d297ac47..bd4b0c9f5f 100644 --- a/app/assets/stylesheets/desktop/topic.scss +++ b/app/assets/stylesheets/desktop/topic.scss @@ -79,7 +79,7 @@ } } -#topic-status-info { +.topic-status-info { border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%); padding-top: 10px; height: 20px; diff --git a/app/assets/stylesheets/embed.css.scss b/app/assets/stylesheets/embed.scss similarity index 97% rename from app/assets/stylesheets/embed.css.scss rename to app/assets/stylesheets/embed.scss index afb4a40c40..66af74d3d0 100644 --- a/app/assets/stylesheets/embed.css.scss +++ b/app/assets/stylesheets/embed.scss @@ -1,6 +1,5 @@ -//= require ./vendor/normalize -//= require ./common/foundation/base - +@import "./vendor/normalize"; +@import "./common/foundation/base"; @import "./common/foundation/variables"; @import "./common/foundation/colors"; @import "./common/foundation/mixins"; diff --git a/app/assets/stylesheets/mobile.scss b/app/assets/stylesheets/mobile.scss index ce220071ee..b1592b6d6a 100644 --- a/app/assets/stylesheets/mobile.scss +++ b/app/assets/stylesheets/mobile.scss @@ -24,7 +24,7 @@ @import "mobile/ring"; @import "mobile/group"; -/* These files doesn't actually exist, they are injected by DiscourseSassImporter. */ +/* These files doesn't actually exist, they are injected by Stylesheet::Compiler. */ @import "plugins"; @import "plugins_mobile"; diff --git a/app/assets/stylesheets/mobile/topic.scss b/app/assets/stylesheets/mobile/topic.scss index 4365009a6a..7e0fc0cac6 100644 --- a/app/assets/stylesheets/mobile/topic.scss +++ b/app/assets/stylesheets/mobile/topic.scss @@ -43,7 +43,7 @@ clear: both; } -#topic-status-info { +.topic-status-info { margin-left: 10px; } diff --git a/app/assets/stylesheets/vendor/font_awesome/_icons.scss b/app/assets/stylesheets/vendor/font_awesome/_icons.scss index 6f9375989a..e63e702c4d 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_icons.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_icons.scss @@ -438,7 +438,7 @@ .#{$fa-css-prefix}-stumbleupon:before { content: $fa-var-stumbleupon; } .#{$fa-css-prefix}-delicious:before { content: $fa-var-delicious; } .#{$fa-css-prefix}-digg:before { content: $fa-var-digg; } -.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; } +.#{$fa-css-prefix}-pied-piper-pp:before { content: $fa-var-pied-piper-pp; } .#{$fa-css-prefix}-pied-piper-alt:before { content: $fa-var-pied-piper-alt; } .#{$fa-css-prefix}-drupal:before { content: $fa-var-drupal; } .#{$fa-css-prefix}-joomla:before { content: $fa-var-joomla; } @@ -488,6 +488,7 @@ .#{$fa-css-prefix}-life-ring:before { content: $fa-var-life-ring; } .#{$fa-css-prefix}-circle-o-notch:before { content: $fa-var-circle-o-notch; } .#{$fa-css-prefix}-ra:before, +.#{$fa-css-prefix}-resistance:before, .#{$fa-css-prefix}-rebel:before { content: $fa-var-rebel; } .#{$fa-css-prefix}-ge:before, .#{$fa-css-prefix}-empire:before { content: $fa-var-empire; } @@ -604,6 +605,7 @@ .#{$fa-css-prefix}-opencart:before { content: $fa-var-opencart; } .#{$fa-css-prefix}-expeditedssl:before { content: $fa-var-expeditedssl; } .#{$fa-css-prefix}-battery-4:before, +.#{$fa-css-prefix}-battery:before, .#{$fa-css-prefix}-battery-full:before { content: $fa-var-battery-full; } .#{$fa-css-prefix}-battery-3:before, .#{$fa-css-prefix}-battery-three-quarters:before { content: $fa-var-battery-three-quarters; } @@ -695,3 +697,93 @@ .#{$fa-css-prefix}-bluetooth:before { content: $fa-var-bluetooth; } .#{$fa-css-prefix}-bluetooth-b:before { content: $fa-var-bluetooth-b; } .#{$fa-css-prefix}-percent:before { content: $fa-var-percent; } +.#{$fa-css-prefix}-gitlab:before { content: $fa-var-gitlab; } +.#{$fa-css-prefix}-wpbeginner:before { content: $fa-var-wpbeginner; } +.#{$fa-css-prefix}-wpforms:before { content: $fa-var-wpforms; } +.#{$fa-css-prefix}-envira:before { content: $fa-var-envira; } +.#{$fa-css-prefix}-universal-access:before { content: $fa-var-universal-access; } +.#{$fa-css-prefix}-wheelchair-alt:before { content: $fa-var-wheelchair-alt; } +.#{$fa-css-prefix}-question-circle-o:before { content: $fa-var-question-circle-o; } +.#{$fa-css-prefix}-blind:before { content: $fa-var-blind; } +.#{$fa-css-prefix}-audio-description:before { content: $fa-var-audio-description; } +.#{$fa-css-prefix}-volume-control-phone:before { content: $fa-var-volume-control-phone; } +.#{$fa-css-prefix}-braille:before { content: $fa-var-braille; } +.#{$fa-css-prefix}-assistive-listening-systems:before { content: $fa-var-assistive-listening-systems; } +.#{$fa-css-prefix}-asl-interpreting:before, +.#{$fa-css-prefix}-american-sign-language-interpreting:before { content: $fa-var-american-sign-language-interpreting; } +.#{$fa-css-prefix}-deafness:before, +.#{$fa-css-prefix}-hard-of-hearing:before, +.#{$fa-css-prefix}-deaf:before { content: $fa-var-deaf; } +.#{$fa-css-prefix}-glide:before { content: $fa-var-glide; } +.#{$fa-css-prefix}-glide-g:before { content: $fa-var-glide-g; } +.#{$fa-css-prefix}-signing:before, +.#{$fa-css-prefix}-sign-language:before { content: $fa-var-sign-language; } +.#{$fa-css-prefix}-low-vision:before { content: $fa-var-low-vision; } +.#{$fa-css-prefix}-viadeo:before { content: $fa-var-viadeo; } +.#{$fa-css-prefix}-viadeo-square:before { content: $fa-var-viadeo-square; } +.#{$fa-css-prefix}-snapchat:before { content: $fa-var-snapchat; } +.#{$fa-css-prefix}-snapchat-ghost:before { content: $fa-var-snapchat-ghost; } +.#{$fa-css-prefix}-snapchat-square:before { content: $fa-var-snapchat-square; } +.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; } +.#{$fa-css-prefix}-first-order:before { content: $fa-var-first-order; } +.#{$fa-css-prefix}-yoast:before { content: $fa-var-yoast; } +.#{$fa-css-prefix}-themeisle:before { content: $fa-var-themeisle; } +.#{$fa-css-prefix}-google-plus-circle:before, +.#{$fa-css-prefix}-google-plus-official:before { content: $fa-var-google-plus-official; } +.#{$fa-css-prefix}-fa:before, +.#{$fa-css-prefix}-font-awesome:before { content: $fa-var-font-awesome; } +.#{$fa-css-prefix}-handshake-o:before { content: $fa-var-handshake-o; } +.#{$fa-css-prefix}-envelope-open:before { content: $fa-var-envelope-open; } +.#{$fa-css-prefix}-envelope-open-o:before { content: $fa-var-envelope-open-o; } +.#{$fa-css-prefix}-linode:before { content: $fa-var-linode; } +.#{$fa-css-prefix}-address-book:before { content: $fa-var-address-book; } +.#{$fa-css-prefix}-address-book-o:before { content: $fa-var-address-book-o; } +.#{$fa-css-prefix}-vcard:before, +.#{$fa-css-prefix}-address-card:before { content: $fa-var-address-card; } +.#{$fa-css-prefix}-vcard-o:before, +.#{$fa-css-prefix}-address-card-o:before { content: $fa-var-address-card-o; } +.#{$fa-css-prefix}-user-circle:before { content: $fa-var-user-circle; } +.#{$fa-css-prefix}-user-circle-o:before { content: $fa-var-user-circle-o; } +.#{$fa-css-prefix}-user-o:before { content: $fa-var-user-o; } +.#{$fa-css-prefix}-id-badge:before { content: $fa-var-id-badge; } +.#{$fa-css-prefix}-drivers-license:before, +.#{$fa-css-prefix}-id-card:before { content: $fa-var-id-card; } +.#{$fa-css-prefix}-drivers-license-o:before, +.#{$fa-css-prefix}-id-card-o:before { content: $fa-var-id-card-o; } +.#{$fa-css-prefix}-quora:before { content: $fa-var-quora; } +.#{$fa-css-prefix}-free-code-camp:before { content: $fa-var-free-code-camp; } +.#{$fa-css-prefix}-telegram:before { content: $fa-var-telegram; } +.#{$fa-css-prefix}-thermometer-4:before, +.#{$fa-css-prefix}-thermometer:before, +.#{$fa-css-prefix}-thermometer-full:before { content: $fa-var-thermometer-full; } +.#{$fa-css-prefix}-thermometer-3:before, +.#{$fa-css-prefix}-thermometer-three-quarters:before { content: $fa-var-thermometer-three-quarters; } +.#{$fa-css-prefix}-thermometer-2:before, +.#{$fa-css-prefix}-thermometer-half:before { content: $fa-var-thermometer-half; } +.#{$fa-css-prefix}-thermometer-1:before, +.#{$fa-css-prefix}-thermometer-quarter:before { content: $fa-var-thermometer-quarter; } +.#{$fa-css-prefix}-thermometer-0:before, +.#{$fa-css-prefix}-thermometer-empty:before { content: $fa-var-thermometer-empty; } +.#{$fa-css-prefix}-shower:before { content: $fa-var-shower; } +.#{$fa-css-prefix}-bathtub:before, +.#{$fa-css-prefix}-s15:before, +.#{$fa-css-prefix}-bath:before { content: $fa-var-bath; } +.#{$fa-css-prefix}-podcast:before { content: $fa-var-podcast; } +.#{$fa-css-prefix}-window-maximize:before { content: $fa-var-window-maximize; } +.#{$fa-css-prefix}-window-minimize:before { content: $fa-var-window-minimize; } +.#{$fa-css-prefix}-window-restore:before { content: $fa-var-window-restore; } +.#{$fa-css-prefix}-times-rectangle:before, +.#{$fa-css-prefix}-window-close:before { content: $fa-var-window-close; } +.#{$fa-css-prefix}-times-rectangle-o:before, +.#{$fa-css-prefix}-window-close-o:before { content: $fa-var-window-close-o; } +.#{$fa-css-prefix}-bandcamp:before { content: $fa-var-bandcamp; } +.#{$fa-css-prefix}-grav:before { content: $fa-var-grav; } +.#{$fa-css-prefix}-etsy:before { content: $fa-var-etsy; } +.#{$fa-css-prefix}-imdb:before { content: $fa-var-imdb; } +.#{$fa-css-prefix}-ravelry:before { content: $fa-var-ravelry; } +.#{$fa-css-prefix}-eercast:before { content: $fa-var-eercast; } +.#{$fa-css-prefix}-microchip:before { content: $fa-var-microchip; } +.#{$fa-css-prefix}-snowflake-o:before { content: $fa-var-snowflake-o; } +.#{$fa-css-prefix}-superpowers:before { content: $fa-var-superpowers; } +.#{$fa-css-prefix}-wpexplorer:before { content: $fa-var-wpexplorer; } +.#{$fa-css-prefix}-meetup:before { content: $fa-var-meetup; } diff --git a/app/assets/stylesheets/vendor/font_awesome/_mixins.scss b/app/assets/stylesheets/vendor/font_awesome/_mixins.scss index 02deee1818..c3bbd5745d 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_mixins.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_mixins.scss @@ -12,15 +12,49 @@ } @mixin fa-icon-rotate($degrees, $rotation) { - filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; -webkit-transform: rotate($degrees); -ms-transform: rotate($degrees); transform: rotate($degrees); } @mixin fa-icon-flip($horiz, $vert, $rotation) { - filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; -webkit-transform: scale($horiz, $vert); -ms-transform: scale($horiz, $vert); transform: scale($horiz, $vert); } + + +// Only display content to screen readers. A la Bootstrap 4. +// +// See: http://a11yproject.com/posts/how-to-hide-content/ + +@mixin sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} + +// Use in conjunction with .sr-only to only display content when it's focused. +// +// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 +// +// Credit: HTML5 Boilerplate + +@mixin sr-only-focusable { + &:active, + &:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; + } +} diff --git a/app/assets/stylesheets/vendor/font_awesome/_path.scss b/app/assets/stylesheets/vendor/font_awesome/_path.scss index 4ec8fc7333..bb457c23a8 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_path.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_path.scss @@ -3,11 +3,13 @@ @font-face { font-family: 'FontAwesome'; - src: url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), - url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'); - // the below are for really ancient browsers such as IE9 and earlier, and I think can be removed - //url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), - //url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'), - //url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), + src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); + src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), + url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), + url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), + url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), + url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts + font-weight: normal; + font-style: normal; } diff --git a/app/assets/stylesheets/vendor/font_awesome/_screen-reader.scss b/app/assets/stylesheets/vendor/font_awesome/_screen-reader.scss new file mode 100644 index 0000000000..637426f0da --- /dev/null +++ b/app/assets/stylesheets/vendor/font_awesome/_screen-reader.scss @@ -0,0 +1,5 @@ +// Screen Readers +// ------------------------- + +.sr-only { @include sr-only(); } +.sr-only-focusable { @include sr-only-focusable(); } diff --git a/app/assets/stylesheets/vendor/font_awesome/_variables.scss b/app/assets/stylesheets/vendor/font_awesome/_variables.scss index 0a471102c4..498fc4a087 100644 --- a/app/assets/stylesheets/vendor/font_awesome/_variables.scss +++ b/app/assets/stylesheets/vendor/font_awesome/_variables.scss @@ -4,14 +4,18 @@ $fa-font-path: "../fonts" !default; $fa-font-size-base: 14px !default; $fa-line-height-base: 1 !default; -//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.5.0/fonts" !default; // for referencing Bootstrap CDN font files directly +//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts" !default; // for referencing Bootstrap CDN font files directly $fa-css-prefix: fa !default; -$fa-version: "4.5.0" !default; +$fa-version: "4.7.0" !default; $fa-border-color: #eee !default; $fa-inverse: #fff !default; $fa-li-width: (30em / 14) !default; $fa-var-500px: "\f26e"; +$fa-var-address-book: "\f2b9"; +$fa-var-address-book-o: "\f2ba"; +$fa-var-address-card: "\f2bb"; +$fa-var-address-card-o: "\f2bc"; $fa-var-adjust: "\f042"; $fa-var-adn: "\f170"; $fa-var-align-center: "\f037"; @@ -20,6 +24,7 @@ $fa-var-align-left: "\f036"; $fa-var-align-right: "\f038"; $fa-var-amazon: "\f270"; $fa-var-ambulance: "\f0f9"; +$fa-var-american-sign-language-interpreting: "\f2a3"; $fa-var-anchor: "\f13d"; $fa-var-android: "\f17b"; $fa-var-angellist: "\f209"; @@ -50,17 +55,24 @@ $fa-var-arrows: "\f047"; $fa-var-arrows-alt: "\f0b2"; $fa-var-arrows-h: "\f07e"; $fa-var-arrows-v: "\f07d"; +$fa-var-asl-interpreting: "\f2a3"; +$fa-var-assistive-listening-systems: "\f2a2"; $fa-var-asterisk: "\f069"; $fa-var-at: "\f1fa"; +$fa-var-audio-description: "\f29e"; $fa-var-automobile: "\f1b9"; $fa-var-backward: "\f04a"; $fa-var-balance-scale: "\f24e"; $fa-var-ban: "\f05e"; +$fa-var-bandcamp: "\f2d5"; $fa-var-bank: "\f19c"; $fa-var-bar-chart: "\f080"; $fa-var-bar-chart-o: "\f080"; $fa-var-barcode: "\f02a"; $fa-var-bars: "\f0c9"; +$fa-var-bath: "\f2cd"; +$fa-var-bathtub: "\f2cd"; +$fa-var-battery: "\f240"; $fa-var-battery-0: "\f244"; $fa-var-battery-1: "\f243"; $fa-var-battery-2: "\f242"; @@ -86,6 +98,7 @@ $fa-var-bitbucket: "\f171"; $fa-var-bitbucket-square: "\f172"; $fa-var-bitcoin: "\f15a"; $fa-var-black-tie: "\f27e"; +$fa-var-blind: "\f29d"; $fa-var-bluetooth: "\f293"; $fa-var-bluetooth-b: "\f294"; $fa-var-bold: "\f032"; @@ -94,6 +107,7 @@ $fa-var-bomb: "\f1e2"; $fa-var-book: "\f02d"; $fa-var-bookmark: "\f02e"; $fa-var-bookmark-o: "\f097"; +$fa-var-braille: "\f2a1"; $fa-var-briefcase: "\f0b1"; $fa-var-btc: "\f15a"; $fa-var-bug: "\f188"; @@ -196,6 +210,8 @@ $fa-var-cutlery: "\f0f5"; $fa-var-dashboard: "\f0e4"; $fa-var-dashcube: "\f210"; $fa-var-database: "\f1c0"; +$fa-var-deaf: "\f2a4"; +$fa-var-deafness: "\f2a4"; $fa-var-dedent: "\f03b"; $fa-var-delicious: "\f1a5"; $fa-var-desktop: "\f108"; @@ -206,18 +222,25 @@ $fa-var-dollar: "\f155"; $fa-var-dot-circle-o: "\f192"; $fa-var-download: "\f019"; $fa-var-dribbble: "\f17d"; +$fa-var-drivers-license: "\f2c2"; +$fa-var-drivers-license-o: "\f2c3"; $fa-var-dropbox: "\f16b"; $fa-var-drupal: "\f1a9"; $fa-var-edge: "\f282"; $fa-var-edit: "\f044"; +$fa-var-eercast: "\f2da"; $fa-var-eject: "\f052"; $fa-var-ellipsis-h: "\f141"; $fa-var-ellipsis-v: "\f142"; $fa-var-empire: "\f1d1"; $fa-var-envelope: "\f0e0"; $fa-var-envelope-o: "\f003"; +$fa-var-envelope-open: "\f2b6"; +$fa-var-envelope-open-o: "\f2b7"; $fa-var-envelope-square: "\f199"; +$fa-var-envira: "\f299"; $fa-var-eraser: "\f12d"; +$fa-var-etsy: "\f2d7"; $fa-var-eur: "\f153"; $fa-var-euro: "\f153"; $fa-var-exchange: "\f0ec"; @@ -231,6 +254,7 @@ $fa-var-external-link-square: "\f14c"; $fa-var-eye: "\f06e"; $fa-var-eye-slash: "\f070"; $fa-var-eyedropper: "\f1fb"; +$fa-var-fa: "\f2b4"; $fa-var-facebook: "\f09a"; $fa-var-facebook-f: "\f09a"; $fa-var-facebook-official: "\f230"; @@ -265,6 +289,7 @@ $fa-var-filter: "\f0b0"; $fa-var-fire: "\f06d"; $fa-var-fire-extinguisher: "\f134"; $fa-var-firefox: "\f269"; +$fa-var-first-order: "\f2b0"; $fa-var-flag: "\f024"; $fa-var-flag-checkered: "\f11e"; $fa-var-flag-o: "\f11d"; @@ -277,11 +302,13 @@ $fa-var-folder-o: "\f114"; $fa-var-folder-open: "\f07c"; $fa-var-folder-open-o: "\f115"; $fa-var-font: "\f031"; +$fa-var-font-awesome: "\f2b4"; $fa-var-fonticons: "\f280"; $fa-var-fort-awesome: "\f286"; $fa-var-forumbee: "\f211"; $fa-var-forward: "\f04e"; $fa-var-foursquare: "\f180"; +$fa-var-free-code-camp: "\f2c5"; $fa-var-frown-o: "\f119"; $fa-var-futbol-o: "\f1e3"; $fa-var-gamepad: "\f11b"; @@ -300,15 +327,21 @@ $fa-var-git-square: "\f1d2"; $fa-var-github: "\f09b"; $fa-var-github-alt: "\f113"; $fa-var-github-square: "\f092"; +$fa-var-gitlab: "\f296"; $fa-var-gittip: "\f184"; $fa-var-glass: "\f000"; +$fa-var-glide: "\f2a5"; +$fa-var-glide-g: "\f2a6"; $fa-var-globe: "\f0ac"; $fa-var-google: "\f1a0"; $fa-var-google-plus: "\f0d5"; +$fa-var-google-plus-circle: "\f2b3"; +$fa-var-google-plus-official: "\f2b3"; $fa-var-google-plus-square: "\f0d4"; $fa-var-google-wallet: "\f1ee"; $fa-var-graduation-cap: "\f19d"; $fa-var-gratipay: "\f184"; +$fa-var-grav: "\f2d6"; $fa-var-group: "\f0c0"; $fa-var-h-square: "\f0fd"; $fa-var-hacker-news: "\f1d4"; @@ -325,6 +358,8 @@ $fa-var-hand-rock-o: "\f255"; $fa-var-hand-scissors-o: "\f257"; $fa-var-hand-spock-o: "\f259"; $fa-var-hand-stop-o: "\f256"; +$fa-var-handshake-o: "\f2b5"; +$fa-var-hard-of-hearing: "\f2a4"; $fa-var-hashtag: "\f292"; $fa-var-hdd-o: "\f0a0"; $fa-var-header: "\f1dc"; @@ -347,8 +382,12 @@ $fa-var-hourglass-start: "\f251"; $fa-var-houzz: "\f27c"; $fa-var-html5: "\f13b"; $fa-var-i-cursor: "\f246"; +$fa-var-id-badge: "\f2c1"; +$fa-var-id-card: "\f2c2"; +$fa-var-id-card-o: "\f2c3"; $fa-var-ils: "\f20b"; $fa-var-image: "\f03e"; +$fa-var-imdb: "\f2d8"; $fa-var-inbox: "\f01c"; $fa-var-indent: "\f03c"; $fa-var-industry: "\f275"; @@ -386,6 +425,7 @@ $fa-var-line-chart: "\f201"; $fa-var-link: "\f0c1"; $fa-var-linkedin: "\f0e1"; $fa-var-linkedin-square: "\f08c"; +$fa-var-linode: "\f2b8"; $fa-var-linux: "\f17c"; $fa-var-list: "\f03a"; $fa-var-list-alt: "\f022"; @@ -397,6 +437,7 @@ $fa-var-long-arrow-down: "\f175"; $fa-var-long-arrow-left: "\f177"; $fa-var-long-arrow-right: "\f178"; $fa-var-long-arrow-up: "\f176"; +$fa-var-low-vision: "\f2a8"; $fa-var-magic: "\f0d0"; $fa-var-magnet: "\f076"; $fa-var-mail-forward: "\f064"; @@ -417,8 +458,10 @@ $fa-var-maxcdn: "\f136"; $fa-var-meanpath: "\f20c"; $fa-var-medium: "\f23a"; $fa-var-medkit: "\f0fa"; +$fa-var-meetup: "\f2e0"; $fa-var-meh-o: "\f11a"; $fa-var-mercury: "\f223"; +$fa-var-microchip: "\f2db"; $fa-var-microphone: "\f130"; $fa-var-microphone-slash: "\f131"; $fa-var-minus: "\f068"; @@ -468,8 +511,9 @@ $fa-var-phone-square: "\f098"; $fa-var-photo: "\f03e"; $fa-var-picture-o: "\f03e"; $fa-var-pie-chart: "\f200"; -$fa-var-pied-piper: "\f1a7"; +$fa-var-pied-piper: "\f2ae"; $fa-var-pied-piper-alt: "\f1a8"; +$fa-var-pied-piper-pp: "\f1a7"; $fa-var-pinterest: "\f0d2"; $fa-var-pinterest-p: "\f231"; $fa-var-pinterest-square: "\f0d3"; @@ -482,6 +526,7 @@ $fa-var-plus: "\f067"; $fa-var-plus-circle: "\f055"; $fa-var-plus-square: "\f0fe"; $fa-var-plus-square-o: "\f196"; +$fa-var-podcast: "\f2ce"; $fa-var-power-off: "\f011"; $fa-var-print: "\f02f"; $fa-var-product-hunt: "\f288"; @@ -490,10 +535,13 @@ $fa-var-qq: "\f1d6"; $fa-var-qrcode: "\f029"; $fa-var-question: "\f128"; $fa-var-question-circle: "\f059"; +$fa-var-question-circle-o: "\f29c"; +$fa-var-quora: "\f2c4"; $fa-var-quote-left: "\f10d"; $fa-var-quote-right: "\f10e"; $fa-var-ra: "\f1d0"; $fa-var-random: "\f074"; +$fa-var-ravelry: "\f2d9"; $fa-var-rebel: "\f1d0"; $fa-var-recycle: "\f1b8"; $fa-var-reddit: "\f1a1"; @@ -507,6 +555,7 @@ $fa-var-reorder: "\f0c9"; $fa-var-repeat: "\f01e"; $fa-var-reply: "\f112"; $fa-var-reply-all: "\f122"; +$fa-var-resistance: "\f1d0"; $fa-var-retweet: "\f079"; $fa-var-rmb: "\f157"; $fa-var-road: "\f018"; @@ -519,6 +568,7 @@ $fa-var-rss-square: "\f143"; $fa-var-rub: "\f158"; $fa-var-ruble: "\f158"; $fa-var-rupee: "\f156"; +$fa-var-s15: "\f2cd"; $fa-var-safari: "\f267"; $fa-var-save: "\f0c7"; $fa-var-scissors: "\f0c4"; @@ -543,9 +593,12 @@ $fa-var-shirtsinbulk: "\f214"; $fa-var-shopping-bag: "\f290"; $fa-var-shopping-basket: "\f291"; $fa-var-shopping-cart: "\f07a"; +$fa-var-shower: "\f2cc"; $fa-var-sign-in: "\f090"; +$fa-var-sign-language: "\f2a7"; $fa-var-sign-out: "\f08b"; $fa-var-signal: "\f012"; +$fa-var-signing: "\f2a7"; $fa-var-simplybuilt: "\f215"; $fa-var-sitemap: "\f0e8"; $fa-var-skyatlas: "\f216"; @@ -554,6 +607,10 @@ $fa-var-slack: "\f198"; $fa-var-sliders: "\f1de"; $fa-var-slideshare: "\f1e7"; $fa-var-smile-o: "\f118"; +$fa-var-snapchat: "\f2ab"; +$fa-var-snapchat-ghost: "\f2ac"; +$fa-var-snapchat-square: "\f2ad"; +$fa-var-snowflake-o: "\f2dc"; $fa-var-soccer-ball-o: "\f1e3"; $fa-var-sort: "\f0dc"; $fa-var-sort-alpha-asc: "\f15d"; @@ -599,6 +656,7 @@ $fa-var-subscript: "\f12c"; $fa-var-subway: "\f239"; $fa-var-suitcase: "\f0f2"; $fa-var-sun-o: "\f185"; +$fa-var-superpowers: "\f2dd"; $fa-var-superscript: "\f12b"; $fa-var-support: "\f1cd"; $fa-var-table: "\f0ce"; @@ -608,6 +666,7 @@ $fa-var-tag: "\f02b"; $fa-var-tags: "\f02c"; $fa-var-tasks: "\f0ae"; $fa-var-taxi: "\f1ba"; +$fa-var-telegram: "\f2c6"; $fa-var-television: "\f26c"; $fa-var-tencent-weibo: "\f1d5"; $fa-var-terminal: "\f120"; @@ -616,6 +675,18 @@ $fa-var-text-width: "\f035"; $fa-var-th: "\f00a"; $fa-var-th-large: "\f009"; $fa-var-th-list: "\f00b"; +$fa-var-themeisle: "\f2b2"; +$fa-var-thermometer: "\f2c7"; +$fa-var-thermometer-0: "\f2cb"; +$fa-var-thermometer-1: "\f2ca"; +$fa-var-thermometer-2: "\f2c9"; +$fa-var-thermometer-3: "\f2c8"; +$fa-var-thermometer-4: "\f2c7"; +$fa-var-thermometer-empty: "\f2cb"; +$fa-var-thermometer-full: "\f2c7"; +$fa-var-thermometer-half: "\f2c9"; +$fa-var-thermometer-quarter: "\f2ca"; +$fa-var-thermometer-three-quarters: "\f2c8"; $fa-var-thumb-tack: "\f08d"; $fa-var-thumbs-down: "\f165"; $fa-var-thumbs-o-down: "\f088"; @@ -625,6 +696,8 @@ $fa-var-ticket: "\f145"; $fa-var-times: "\f00d"; $fa-var-times-circle: "\f057"; $fa-var-times-circle-o: "\f05c"; +$fa-var-times-rectangle: "\f2d3"; +$fa-var-times-rectangle-o: "\f2d4"; $fa-var-tint: "\f043"; $fa-var-toggle-down: "\f150"; $fa-var-toggle-left: "\f191"; @@ -655,6 +728,7 @@ $fa-var-twitter-square: "\f081"; $fa-var-umbrella: "\f0e9"; $fa-var-underline: "\f0cd"; $fa-var-undo: "\f0e2"; +$fa-var-universal-access: "\f29a"; $fa-var-university: "\f19c"; $fa-var-unlink: "\f127"; $fa-var-unlock: "\f09c"; @@ -664,20 +738,28 @@ $fa-var-upload: "\f093"; $fa-var-usb: "\f287"; $fa-var-usd: "\f155"; $fa-var-user: "\f007"; +$fa-var-user-circle: "\f2bd"; +$fa-var-user-circle-o: "\f2be"; $fa-var-user-md: "\f0f0"; +$fa-var-user-o: "\f2c0"; $fa-var-user-plus: "\f234"; $fa-var-user-secret: "\f21b"; $fa-var-user-times: "\f235"; $fa-var-users: "\f0c0"; +$fa-var-vcard: "\f2bb"; +$fa-var-vcard-o: "\f2bc"; $fa-var-venus: "\f221"; $fa-var-venus-double: "\f226"; $fa-var-venus-mars: "\f228"; $fa-var-viacoin: "\f237"; +$fa-var-viadeo: "\f2a9"; +$fa-var-viadeo-square: "\f2aa"; $fa-var-video-camera: "\f03d"; $fa-var-vimeo: "\f27d"; $fa-var-vimeo-square: "\f194"; $fa-var-vine: "\f1ca"; $fa-var-vk: "\f189"; +$fa-var-volume-control-phone: "\f2a0"; $fa-var-volume-down: "\f027"; $fa-var-volume-off: "\f026"; $fa-var-volume-up: "\f028"; @@ -687,11 +769,20 @@ $fa-var-weibo: "\f18a"; $fa-var-weixin: "\f1d7"; $fa-var-whatsapp: "\f232"; $fa-var-wheelchair: "\f193"; +$fa-var-wheelchair-alt: "\f29b"; $fa-var-wifi: "\f1eb"; $fa-var-wikipedia-w: "\f266"; +$fa-var-window-close: "\f2d3"; +$fa-var-window-close-o: "\f2d4"; +$fa-var-window-maximize: "\f2d0"; +$fa-var-window-minimize: "\f2d1"; +$fa-var-window-restore: "\f2d2"; $fa-var-windows: "\f17a"; $fa-var-won: "\f159"; $fa-var-wordpress: "\f19a"; +$fa-var-wpbeginner: "\f297"; +$fa-var-wpexplorer: "\f2de"; +$fa-var-wpforms: "\f298"; $fa-var-wrench: "\f0ad"; $fa-var-xing: "\f168"; $fa-var-xing-square: "\f169"; @@ -702,6 +793,7 @@ $fa-var-yc: "\f23b"; $fa-var-yc-square: "\f1d4"; $fa-var-yelp: "\f1e9"; $fa-var-yen: "\f157"; +$fa-var-yoast: "\f2b1"; $fa-var-youtube: "\f167"; $fa-var-youtube-play: "\f16a"; $fa-var-youtube-square: "\f166"; diff --git a/app/assets/stylesheets/vendor/font_awesome/font-awesome.scss b/app/assets/stylesheets/vendor/font_awesome/font-awesome.scss index 4cd98d36f4..abab424ee5 100644 --- a/app/assets/stylesheets/vendor/font_awesome/font-awesome.scss +++ b/app/assets/stylesheets/vendor/font_awesome/font-awesome.scss @@ -1,5 +1,5 @@ /*! - * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ @@ -15,3 +15,4 @@ @import "rotated-flipped"; @import "stacked"; @import "icons"; +@import "screen-reader"; diff --git a/app/assets/stylesheets/vendor/sweetalert.css b/app/assets/stylesheets/vendor/sweetalert.scss old mode 100755 new mode 100644 similarity index 100% rename from app/assets/stylesheets/vendor/sweetalert.css rename to app/assets/stylesheets/vendor/sweetalert.scss diff --git a/app/controllers/admin/color_schemes_controller.rb b/app/controllers/admin/color_schemes_controller.rb index 35f45c6e4a..dda6c9f07c 100644 --- a/app/controllers/admin/color_schemes_controller.rb +++ b/app/controllers/admin/color_schemes_controller.rb @@ -3,7 +3,7 @@ class Admin::ColorSchemesController < Admin::AdminController before_filter :fetch_color_scheme, only: [:update, :destroy] def index - render_serialized([ColorScheme.base] + ColorScheme.current_version.order('id ASC').all.to_a, ColorSchemeSerializer) + render_serialized(ColorScheme.base_color_schemes + ColorScheme.order('id ASC').all.to_a, ColorSchemeSerializer) end def create @@ -37,6 +37,6 @@ class Admin::ColorSchemesController < Admin::AdminController end def color_scheme_params - params.permit(color_scheme: [:enabled, :name, colors: [:name, :hex]])[:color_scheme] + params.permit(color_scheme: [:base_scheme_id, :name, colors: [:name, :hex]])[:color_scheme] end end diff --git a/app/controllers/admin/email_controller.rb b/app/controllers/admin/email_controller.rb index 74ce0901ec..455a22151e 100644 --- a/app/controllers/admin/email_controller.rb +++ b/app/controllers/admin/email_controller.rb @@ -84,8 +84,21 @@ class Admin::EmailController < Admin::AdminController def handle_mail params.require(:email) - Email::Processor.process!(params[:email]) - render plain: "email was processed" + retry_count = 0 + + begin + Jobs.enqueue(:process_email, mail: params[:email], retry_on_rate_limit: true) + rescue JSON::GeneratorError => e + if retry_count == 0 + params[:email] = params[:email].force_encoding('iso-8859-1').encode("UTF-8") + retry_count += 1 + retry + else + raise e + end + end + + render plain: "email has been received and is queued for processing" end def raw_email diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index d2a93629fc..c93e287cee 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -24,13 +24,22 @@ class Admin::GroupsController < Admin::AdminController def bulk_perform group = Group.find(params[:group_id].to_i) + users_added = 0 if group.present? users = (params[:users] || []).map {|u| u.downcase} - user_ids = User.where("username_lower in (:users) OR email IN (:users)", users: users).pluck(:id) - group.bulk_add(user_ids) if user_ids.present? + valid_emails = {} + valid_usernames = {} + valid_users = User.where("username_lower IN (:users) OR email IN (:users)", users: users).pluck(:id, :username_lower, :email) + valid_users.map! do |id, username_lower, email| + valid_emails[email] = valid_usernames[username_lower] = id + id + end + invalid_users = users.reject! { |u| valid_emails[u] || valid_usernames[u] } + group.bulk_add(valid_users) if valid_users.present? + users_added = valid_users.count end - render json: success_json + render json: { success: true, message: I18n.t('groups.success.bulk_add', users_added: users_added), users_not_added: invalid_users } end def create @@ -71,6 +80,10 @@ class Admin::GroupsController < Admin::AdminController group.bio_raw = group_params[:bio_raw] if group_params[:bio_raw] group.full_name = group_params[:full_name] if group_params[:full_name] + if group_params.key?(:default_notification_level) + group.default_notification_level = group_params[:default_notification_level] + end + if group_params[:allow_membership_requests] group.allow_membership_requests = group_params[:allow_membership_requests] end @@ -150,7 +163,8 @@ class Admin::GroupsController < Admin::AdminController :name, :alias_level, :visible, :automatic_membership_email_domains, :automatic_membership_retroactive, :title, :primary_group, :grant_trust_level, :incoming_email, :flair_url, :flair_bg_color, - :flair_color, :bio_raw, :public, :allow_membership_requests, :full_name + :flair_color, :bio_raw, :public, :allow_membership_requests, :full_name, + :default_notification_level ) end end diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 8a47b8a9d1..6490c96d73 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -9,7 +9,7 @@ class Admin::ReportsController < Admin::AdminController start_date = params[:start_date].present? ? Time.parse(params[:start_date]) : 30.days.ago end_date = params[:end_date].present? ? Time.parse(params[:end_date]) : start_date + 30.days - + if params.has_key?(:category_id) && params[:category_id].to_i > 0 category_id = params[:category_id].to_i else diff --git a/app/controllers/admin/site_customizations_controller.rb b/app/controllers/admin/site_customizations_controller.rb deleted file mode 100644 index afd3c162e5..0000000000 --- a/app/controllers/admin/site_customizations_controller.rb +++ /dev/null @@ -1,92 +0,0 @@ -class Admin::SiteCustomizationsController < Admin::AdminController - - before_filter :enable_customization - - skip_before_filter :check_xhr, only: [:show] - - def index - @site_customizations = SiteCustomization.order(:name) - - respond_to do |format| - format.json { render json: @site_customizations } - end - end - - def create - @site_customization = SiteCustomization.new(site_customization_params) - @site_customization.user_id = current_user.id - - respond_to do |format| - if @site_customization.save - log_site_customization_change(nil, site_customization_params) - format.json { render json: @site_customization, status: :created} - else - format.json { render json: @site_customization.errors, status: :unprocessable_entity } - end - end - end - - def update - @site_customization = SiteCustomization.find(params[:id]) - log_record = log_site_customization_change(@site_customization, site_customization_params) - - respond_to do |format| - if @site_customization.update_attributes(site_customization_params) - format.json { render json: @site_customization, status: :created} - else - log_record.destroy if log_record - format.json { render json: @site_customization.errors, status: :unprocessable_entity } - end - end - end - - def destroy - @site_customization = SiteCustomization.find(params[:id]) - StaffActionLogger.new(current_user).log_site_customization_destroy(@site_customization) - @site_customization.destroy - - respond_to do |format| - format.json { head :no_content } - end - end - - def show - @site_customization = SiteCustomization.find(params[:id]) - - respond_to do |format| - format.json do - check_xhr - render json: SiteCustomizationSerializer.new(@site_customization) - end - - format.any(:html, :text) do - raise RenderEmpty.new if request.xhr? - - response.headers['Content-Disposition'] = "attachment; filename=#{@site_customization.name.parameterize}.dcstyle.json" - response.sending_file = true - render json: SiteCustomizationSerializer.new(@site_customization) - end - end - - end - - private - - def site_customization_params - params.require(:site_customization) - .permit(:name, :stylesheet, :header, :top, :footer, - :mobile_stylesheet, :mobile_header, :mobile_top, :mobile_footer, - :head_tag, :body_tag, - :position, :enabled, :key, - :stylesheet_baked, :embedded_css) - end - - def log_site_customization_change(old_record, new_params) - StaffActionLogger.new(current_user).log_site_customization_change(old_record, new_params) - end - - def enable_customization - session[:disable_customization] = false - end - -end diff --git a/app/controllers/admin/staff_action_logs_controller.rb b/app/controllers/admin/staff_action_logs_controller.rb index 5324aabc0d..b004601014 100644 --- a/app/controllers/admin/staff_action_logs_controller.rb +++ b/app/controllers/admin/staff_action_logs_controller.rb @@ -6,4 +6,73 @@ class Admin::StaffActionLogsController < Admin::AdminController render_serialized(staff_action_logs, UserHistorySerializer) end + def diff + require_dependency "discourse_diff" + + @history = UserHistory.find(params[:id]) + prev = @history.previous_value + cur = @history.new_value + + prev = JSON.parse(prev) if prev + cur = JSON.parse(cur) if cur + + diff_fields = {} + + output = "

    #{CGI.escapeHTML(cur["name"].to_s)}

    " + + diff_fields["name"] = { + prev: prev["name"].to_s, + cur: cur["name"].to_s, + } + + ["default", "user_selectable"].each do |f| + diff_fields[f] = { + prev: (!!prev[f]).to_s, + cur: (!!cur[f]).to_s + } + end + + diff_fields["color scheme"] = { + prev: prev["color_scheme"]&.fetch("name").to_s, + cur: cur["color_scheme"]&.fetch("name").to_s, + } + + diff_fields["included themes"] = { + prev: child_themes(prev), + cur: child_themes(cur) + } + + + load_diff(diff_fields, :cur, cur) + load_diff(diff_fields, :prev, prev) + + diff_fields.delete_if{|k,v| v[:cur] == v[:prev]} + + + diff_fields.each do |k,v| + output << "

    #{k}

    " + diff = DiscourseDiff.new(v[:prev] || "", v[:cur] || "") + output << diff.side_by_side_markdown + end + + render json: {side_by_side: output} + end + + protected + + def child_themes(theme) + return "" unless children = theme["child_themes"] + + children.map{|row| row["name"]}.join(" ").to_s + end + + def load_diff(hash, key, val) + if f=val["theme_fields"] + f.each do |row| + entry = hash[row["target"] + " " + row["name"]] ||= {} + entry[key] = row["value"] + end + end + end + end diff --git a/app/controllers/admin/themes_controller.rb b/app/controllers/admin/themes_controller.rb new file mode 100644 index 0000000000..700df88b65 --- /dev/null +++ b/app/controllers/admin/themes_controller.rb @@ -0,0 +1,205 @@ +class Admin::ThemesController < Admin::AdminController + + skip_before_filter :check_xhr, only: [:show, :preview] + + def preview + @theme = Theme.find(params[:id]) + + redirect_to path("/"), flash: {preview_theme_key: @theme.key} + end + + def import + + @theme = nil + if params[:theme] + json = JSON::parse(params[:theme].read) + theme = json['theme'] + + @theme = Theme.new(name: theme["name"], user_id: current_user.id) + theme["theme_fields"]&.each do |field| + @theme.set_field(field["target"], field["name"], field["value"]) + end + + if @theme.save + log_theme_change(nil, @theme) + render json: @theme, status: :created + else + render json: @theme.errors, status: :unprocessable_entity + end + elsif params[:remote] + @theme = RemoteTheme.import_theme(params[:remote]) + render json: @theme, status: :created + else + render json: @theme.errors, status: :unprocessable_entity + end + + end + + def index + @theme = Theme.order(:name).includes(:theme_fields, :remote_theme) + @color_schemes = ColorScheme.all.to_a + light = ColorScheme.new(name: I18n.t("color_schemes.default")) + @color_schemes.unshift(light) + + payload = { + themes: ActiveModel::ArraySerializer.new(@theme, each_serializer: ThemeSerializer), + extras: { + color_schemes: ActiveModel::ArraySerializer.new(@color_schemes, each_serializer: ColorSchemeSerializer) + } + } + + respond_to do |format| + format.json { render json: payload} + end + end + + def create + @theme = Theme.new(name: theme_params[:name], + user_id: current_user.id, + user_selectable: theme_params[:user_selectable] || false, + color_scheme_id: theme_params[:color_scheme_id]) + set_fields + + respond_to do |format| + if @theme.save + update_default_theme + log_theme_change(nil, @theme) + format.json { render json: @theme, status: :created} + else + format.json { render json: @theme.errors, status: :unprocessable_entity } + end + end + end + + def update + @theme = Theme.find(params[:id]) + + original_json = ThemeSerializer.new(@theme, root: false).to_json + + [:name, :color_scheme_id, :user_selectable].each do |field| + if theme_params.key?(field) + @theme.send("#{field}=", theme_params[field]) + end + end + + if theme_params.key?(:child_theme_ids) + expected = theme_params[:child_theme_ids].map(&:to_i) + + @theme.child_theme_relation.to_a.each do |child| + if expected.include?(child.child_theme_id) + expected.reject!{|id| id == child.child_theme_id} + else + child.destroy + end + end + + Theme.where(id: expected).each do |theme| + @theme.add_child_theme!(theme) + end + + end + + set_fields + + save_remote = false + if params[:theme][:remote_check] + @theme.remote_theme.update_remote_version + save_remote = true + end + + if params[:theme][:remote_update] + @theme.remote_theme.update_from_remote + save_remote = true + end + + respond_to do |format| + if @theme.save + + @theme.remote_theme.save! if save_remote + + update_default_theme + + log_theme_change(original_json, @theme) + format.json { render json: @theme, status: :created} + else + format.json { + + error = @theme.errors[:color_scheme] ? I18n.t("themes.bad_color_scheme") : I18n.t("themes.other_error") + render json: {errors: [ error ]}, status: :unprocessable_entity + } + end + end + end + + def destroy + @theme = Theme.find(params[:id]) + StaffActionLogger.new(current_user).log_theme_destroy(@theme) + @theme.destroy + + respond_to do |format| + format.json { head :no_content } + end + end + + def show + @theme = Theme.find(params[:id]) + + respond_to do |format| + format.json do + check_xhr + render json: ThemeSerializer.new(@theme) + end + + format.any(:html, :text) do + raise RenderEmpty.new if request.xhr? + + response.headers['Content-Disposition'] = "attachment; filename=#{@theme.name.parameterize}.dcstyle.json" + response.sending_file = true + render json: ThemeSerializer.new(@theme) + end + end + + end + + private + + def update_default_theme + if theme_params.key?(:default) + is_default = theme_params[:default] + if @theme.key == SiteSetting.default_theme_key && !is_default + Theme.clear_default! + elsif is_default + @theme.set_default! + end + end + end + + def theme_params + @theme_params ||= + begin + # deep munge is a train wreck, work around it for now + params[:theme][:child_theme_ids] ||= [] if params[:theme].key?(:child_theme_ids) + params.require(:theme) + .permit(:name, + :color_scheme_id, + :default, + :user_selectable, + theme_fields: [:name, :target, :value], + child_theme_ids: []) + end + end + + def set_fields + + return unless fields = theme_params[:theme_fields] + + fields.each do |field| + @theme.set_field(field[:target], field[:name], field[:value]) + end + end + + def log_theme_change(old_record, new_record) + StaffActionLogger.new(current_user).log_theme_change(old_record, new_record) + end + +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d7a37a3a45..48b38ed88f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -33,12 +33,11 @@ class ApplicationController < ActionController::Base end end + before_filter :handle_theme before_filter :set_current_user_for_logs before_filter :clear_notifications before_filter :set_locale before_filter :set_mobile_view - before_filter :inject_preview_style - before_filter :disable_customization before_filter :block_if_readonly_mode before_filter :authorize_mini_profiler before_filter :preload_json @@ -243,28 +242,40 @@ class ApplicationController < ActionController::Base session[:mobile_view] = params[:mobile_view] if params.has_key?(:mobile_view) end - def inject_preview_style - style = request['preview-style'] + NO_CUSTOM = "no_custom".freeze + NO_PLUGINS = "no_plugins".freeze + ONLY_OFFICIAL = "only_official".freeze + SAFE_MODE = "safe_mode".freeze - if style.nil? - session[:preview_style] = cookies[:preview_style] - else - cookies.delete(:preview_style) - - if style.blank? || style == 'default' - session[:preview_style] = nil - else - session[:preview_style] = style - if request['sticky'] - cookies[:preview_style] = style - end - end + def resolve_safe_mode + safe_mode = params[SAFE_MODE] + if safe_mode + request.env[NO_CUSTOM] = !!safe_mode.include?(NO_CUSTOM) + request.env[NO_PLUGINS] = !!safe_mode.include?(NO_PLUGINS) + request.env[ONLY_OFFICIAL] = !!safe_mode.include?(ONLY_OFFICIAL) end - end - def disable_customization - session[:disable_customization] = params[:customization] == "0" if params.has_key?(:customization) + def handle_theme + + return if request.xhr? || request.format.json? + return if request.method != "GET" + + resolve_safe_mode + return if request.env[NO_CUSTOM] + + theme_key = flash[:preview_theme_key] || cookies[:theme_key] || session[:theme_key] + + if theme_key && !guardian.allow_theme?(theme_key) + theme_key = nil + cookies[:theme_key] = nil + session[:theme_key] = nil + end + + theme_key ||= SiteSetting.default_theme_key + theme_key = nil if theme_key.blank? + + @theme_key = request.env[:resolved_theme_key] = theme_key end def guardian @@ -410,15 +421,23 @@ class ApplicationController < ActionController::Base def custom_html_json target = view_context.mobile_view? ? :mobile : :desktop - data = { - top: SiteCustomization.custom_top(session[:preview_style], target), - footer: SiteCustomization.custom_footer(session[:preview_style], target) - } + data = if @theme_key + { + top: Theme.lookup_field(@theme_key, target, "after_header"), + footer: Theme.lookup_field(@theme_key, target, "footer") + } + else + {} + end if DiscoursePluginRegistry.custom_html data.merge! DiscoursePluginRegistry.custom_html end + DiscoursePluginRegistry.html_builders.each do |name, blk| + data[name] = blk.call(self) + end + MultiJson.dump(data) end @@ -450,7 +469,7 @@ class ApplicationController < ActionController::Base # type - a machine-readable description of the error # status - HTTP status code to return def render_json_error(obj, opts={}) - opts = { status: opts } if opts.is_a?(Fixnum) + opts = { status: opts } if opts.is_a?(Integer) render json: MultiJson.dump(create_errors_json(obj, opts[:type])), status: opts[:status] || 422 end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 1314a763e2..1a027838d7 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -245,8 +245,13 @@ class GroupsController < ApplicationController group = find_group(:id) notification_level = params.require(:notification_level) + user_id = current_user.id + if guardian.is_staff? + user_id = params[:user_id] || user_id + end + GroupUser.where(group_id: group.id) - .where(user_id: current_user.id) + .where(user_id: user_id) .update_all(notification_level: notification_level) render json: success_json diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index e4990e0af1..dd151b0ba2 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -36,9 +36,7 @@ class InvitesController < ApplicationController user = invite.redeem(username: params[:username], password: params[:password]) if user.present? log_on_user(user) - - # Send a welcome message if required - user.enqueue_welcome_message('welcome_invite') if user.send_welcome_message + post_process_invite(user) end topic = user.present? ? invite.topics.first : nil @@ -128,10 +126,7 @@ class InvitesController < ApplicationController user = Invite.redeem_from_token(params[:token], params[:email], params[:username], params[:name], params[:topic].to_i) if user.present? log_on_user(user) - - # Send a welcome message if required - user.enqueue_welcome_message('welcome_invite') if user.send_welcome_message - + post_process_invite(user) topic = invite.topics.first if topic.present? redirect_to path("#{topic.relative_url}") @@ -223,4 +218,17 @@ class InvitesController < ApplicationController false end end + + private + + def post_process_invite(user) + user.enqueue_welcome_message('welcome_invite') if user.send_welcome_message + if user.has_password? + email_token = user.email_tokens.create(email: user.email) + Jobs.enqueue(:critical_user_email, type: :signup, user_id: user.id, email_token: email_token.token) + elsif !SiteSetting.enable_sso && SiteSetting.enable_local_logins + Jobs.enqueue(:invite_password_instructions_email, username: user.username) + end + end + end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index e6c82e01ec..1415f78f57 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -8,7 +8,7 @@ class NotificationsController < ApplicationController user = if params[:username] && !params[:recent] user_record = User.find_by(username: params[:username].to_s) - raise Discourse::InvalidParameters.new(:username) if !user_record + raise Discourse::NotFound if !user_record user_record else current_user diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 9feab0fe82..6ed83c91eb 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -383,8 +383,8 @@ class PostsController < ApplicationController PostAction.act(current_user, post, PostActionType.types[:bookmark]) else post_action = PostAction.find_by(post_id: params[:post_id], user_id: current_user.id) - post = post_action.post - raise Discourse::InvalidParameters unless post_action + post = post_action&.post + raise Discourse::NotFound unless post_action PostAction.remove_act(current_user, post, PostActionType.types[:bookmark]) end diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 49e7639974..96259d430d 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -137,15 +137,18 @@ class SessionController < ApplicationController rescue ActiveRecord::RecordInvalid => e if SiteSetting.verbose_sso_logging - Rails.logger.warn(<<-EOF) - Verbose SSO log: Record was invalid: #{e.record.class.name} #{e.record.id}\n - #{e.record.errors.to_h}\n - \n - #{sso.diagnostics} + Rails.logger.warn(<<~EOF) + Verbose SSO log: Record was invalid: #{e.record.class.name} #{e.record.id} + #{e.record.errors.to_h} + + Attributes: + #{e.record.attributes.slice(*SingleSignOn::ACCESSORS.map(&:to_s))} + + SSO Diagnostics: + #{sso.diagnostics} EOF end - text = nil # If there's a problem with the email we can explain that diff --git a/app/controllers/site_customizations_controller.rb b/app/controllers/site_customizations_controller.rb deleted file mode 100644 index 34a314720f..0000000000 --- a/app/controllers/site_customizations_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -class SiteCustomizationsController < ApplicationController - skip_before_filter :preload_json, :check_xhr, :redirect_to_login_if_required - - def show - no_cookies - - cache_time = request.env["HTTP_IF_MODIFIED_SINCE"] - cache_time = Time.rfc2822(cache_time) rescue nil if cache_time - stylesheet_time = - begin - if params[:key].to_s == SiteCustomization::ENABLED_KEY - SiteCustomization.where(enabled: true) - .order('created_at desc') - .limit(1) - .pluck(:created_at) - .first - else - SiteCustomization.where(key: params[:key].to_s).pluck(:created_at).first - end - end - - if !stylesheet_time - raise Discourse::NotFound - end - - if cache_time && stylesheet_time <= cache_time - return render nothing: true, status: 304 - end - - response.headers["Last-Modified"] = stylesheet_time.httpdate - expires_in 1.year, public: true - render text: SiteCustomization.stylesheet_contents(params[:key], params[:target]), - content_type: "text/css" - end -end diff --git a/app/controllers/stylesheets_controller.rb b/app/controllers/stylesheets_controller.rb index 55c5166d4f..dea3d5d34c 100644 --- a/app/controllers/stylesheets_controller.rb +++ b/app/controllers/stylesheets_controller.rb @@ -1,12 +1,40 @@ class StylesheetsController < ApplicationController - skip_before_filter :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show] + skip_before_filter :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :show_source_map] + + def show_source_map + show_resource(source_map: true) + end def show + show_resource + end + + protected + + def show_resource(source_map: false) + + extension = source_map ? ".css.map" : ".css" + + params[:name] no_cookies target,digest = params[:name].split(/_([a-f0-9]{40})/) + if Rails.env == "development" + # TODO add theme + # calling this method ensures we have a cache for said target + # we hold of re-compilation till someone asks for asset + if target.include?("theme") + split_target,theme_id = target.split(/_(-?[0-9]+)/) + theme = Theme.find(theme_id) if theme_id + else + split_target,color_scheme_id = target.split(/_(-?[0-9]+)/) + theme = Theme.find_by(color_scheme_id: color_scheme_id) + end + Stylesheet::Manager.stylesheet_link_tag(split_target, nil, theme&.key) + end + cache_time = request.env["HTTP_IF_MODIFIED_SINCE"] cache_time = Time.rfc2822(cache_time) rescue nil if cache_time @@ -19,7 +47,7 @@ class StylesheetsController < ApplicationController # Security note, safe due to route constraint underscore_digest = digest ? "_" + digest : "" - location = "#{Rails.root}/#{DiscourseStylesheets::CACHE_PATH}/#{target}#{underscore_digest}.css" + location = "#{Rails.root}/#{Stylesheet::Manager::CACHE_PATH}/#{target}#{underscore_digest}#{extension}" stylesheet_time = query.pluck(:created_at).first @@ -33,24 +61,31 @@ class StylesheetsController < ApplicationController unless File.exist?(location) - if current = query.first - File.write(location, current.content) + if current = query.limit(1).pluck(source_map ? :source_map : :content).first + File.write(location, current) else raise Discourse::NotFound end end - response.headers['Last-Modified'] = stylesheet_time.httpdate if stylesheet_time - immutable_for(1.year) unless Rails.env == "development" + if Rails.env == "development" + response.headers['Last-Modified'] = Time.zone.now.httpdate + immutable_for(1.second) + else + response.headers['Last-Modified'] = stylesheet_time.httpdate if stylesheet_time + immutable_for(1.year) + end send_file(location, disposition: :inline) end - protected - def handle_missing_cache(location, name, digest) + location = location.sub(".css.map", ".css") + source_map_location = location + ".map" + existing = File.read(location) rescue nil if existing && digest - StylesheetCache.add(name, digest, existing) + source_map = File.read(source_map_location) rescue nil + StylesheetCache.add(name, digest, existing, source_map) end end diff --git a/app/controllers/themes_controller.rb b/app/controllers/themes_controller.rb new file mode 100644 index 0000000000..a5f26c5cc4 --- /dev/null +++ b/app/controllers/themes_controller.rb @@ -0,0 +1,28 @@ +class ThemesController < ::ApplicationController + def assets + theme_key = params[:key].to_s + + if theme_key == "default" + theme_key = nil + else + raise Discourse::NotFound unless Theme.where(key: theme_key).exists? + end + + object = [:mobile, :desktop, :desktop_theme, :mobile_theme].map do |target| + link = Stylesheet::Manager.stylesheet_link_tag(target, 'all', params[:key]) + if link + href = link.split(/["']/)[1] + if Rails.env.development? + href << (href.include?("?") ? "&" : "?") + href << SecureRandom.hex + end + { + target: target, + url: href + } + end + end.compact + + render json: object.as_json + end +end diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index 190c22924f..d7097eb434 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -112,7 +112,8 @@ class Users::OmniauthCallbacksController < ApplicationController def user_found(user) # automatically activate/unstage any account if a provider marked the email valid if @auth_result.email_valid && @auth_result.email == user.email - user.update!(staged: false, active: true) + user.update!(staged: false) + user.activate end if ScreenedIpAddress.should_block?(request.remote_ip) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index eb69ce7d3c..401d9ed623 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -36,7 +36,7 @@ class UsersController < ApplicationController end def show - raise Discourse::InvalidAccess if SiteSetting.hide_user_profiles_from_public && !current_user + return redirect_to path('/login') if SiteSetting.hide_user_profiles_from_public && !current_user @user = fetch_user_from_params( { include_inactive: current_user.try(:staff?) }, @@ -307,7 +307,7 @@ class UsersController < ApplicationController return fail_with("login.email_too_long") end - if SiteSetting.reserved_usernames.split("|").include? params[:username].downcase + if User.reserved_username?(params[:username]) return fail_with("login.reserved_username") end @@ -744,7 +744,7 @@ class UsersController < ApplicationController result = {} - %W{number_of_deleted_posts number_of_flagged_posts number_of_flags_given number_of_suspensions number_of_warnings}.each do |info| + %W{number_of_deleted_posts number_of_flagged_posts number_of_flags_given number_of_suspensions warnings_received_count}.each do |info| result[info] = @user.send(info) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a9e4ed40e8..404ed92bf4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -45,17 +45,17 @@ module ApplicationHelper end end - def script(*args) + def preload_script(script) + path = asset_path("#{script}.js") + if GlobalSetting.cdn_url && GlobalSetting.cdn_url.start_with?("https") && ENV["COMPRESS_BROTLI"] == "1" && request.env["HTTP_ACCEPT_ENCODING"] =~ /br/ - tags = javascript_include_tag(*args) - tags.gsub!("#{GlobalSetting.cdn_url}/assets/", "#{GlobalSetting.cdn_url}/brotli_asset/") - tags.html_safe - else - javascript_include_tag(*args) + path.gsub!("#{GlobalSetting.cdn_url}/assets/", "#{GlobalSetting.cdn_url}/brotli_asset/") end +" +".html_safe end def discourse_csrf_tags @@ -248,32 +248,23 @@ module ApplicationHelper MobileDetection.mobile_device?(request.user_agent) end - NO_CUSTOM = "no_custom".freeze - NO_PLUGINS = "no_plugins".freeze - ONLY_OFFICIAL = "only_official".freeze - SAFE_MODE = "safe_mode".freeze - def customization_disabled? - safe_mode = params[SAFE_MODE] - session[:disable_customization] || (safe_mode && safe_mode.include?(NO_CUSTOM)) + request.env[ApplicationController::NO_CUSTOM] end def allow_plugins? - safe_mode = params[SAFE_MODE] - !(safe_mode && safe_mode.include?(NO_PLUGINS)) + !request.env[ApplicationController::NO_PLUGINS] end def allow_third_party_plugins? - safe_mode = params[SAFE_MODE] - !(safe_mode && (safe_mode.include?(NO_PLUGINS) || safe_mode.include?(ONLY_OFFICIAL))) + allow_plugins? && !request.env[ApplicationController::ONLY_OFFICIAL] end def normalized_safe_mode - mode_string = params["safe_mode"] safe_mode = nil - (safe_mode ||= []) << NO_CUSTOM if mode_string.include?(NO_CUSTOM) - (safe_mode ||= []) << NO_PLUGINS if mode_string.include?(NO_PLUGINS) - (safe_mode ||= []) << ONLY_OFFICIAL if mode_string.include?(ONLY_OFFICIAL) + (safe_mode ||= []) << ApplicationController::NO_CUSTOM if customization_disabled? + (safe_mode ||= []) << ApplicationController::NO_PLUGINS if !allow_plugins? + (safe_mode ||= []) << ApplicationController::ONLY_OFFICIAL if !allow_third_party_plugins? if safe_mode safe_mode.join(",").html_safe end @@ -316,4 +307,32 @@ module ApplicationHelper '' end end + + def theme_key + if customization_disabled? + nil + else + request.env[:resolved_theme_key] + end + end + + def build_plugin_html(name) + return "" unless allow_plugins? + DiscoursePluginRegistry.build_html(name, controller) || "" + end + + def theme_lookup(name) + lookup = Theme.lookup_field(theme_key, mobile_view? ? :mobile : :desktop, name) + lookup.html_safe if lookup + end + + def discourse_stylesheet_link_tag(name, opts={}) + if opts.key?(:theme_key) + key = opts[:theme_key] unless customization_disabled? + else + key = theme_key + end + + Stylesheet::Manager.stylesheet_link_tag(name, 'all', key) + end end diff --git a/app/helpers/email_helper.rb b/app/helpers/email_helper.rb new file mode 100644 index 0000000000..620f1fe94c --- /dev/null +++ b/app/helpers/email_helper.rb @@ -0,0 +1,38 @@ +module EmailHelper + + def mailing_list_topic(topic, post_count) + render( + partial: partial_for("mailing_list_post"), + locals: { topic: topic, post_count: post_count } + ) + end + + def mailing_list_topic_text(topic) + url, title = extract_details(topic) + raw(@markdown_linker.create(title, url)) + end + + def private_topic_title(topic) + I18n.t("system_messages.private_topic_title", id: topic.id) + end + + def email_topic_link(topic) + url, title = extract_details(topic) + raw "#{title}" + end + + protected + + def extract_details(topic) + if SiteSetting.private_email? + [topic.slugless_url, private_topic_title(topic)] + else + [topic.relative_url, format_topic_title(topic.title)] + end + end + + def partial_for(name) + SiteSetting.private_email? ? "email/secure_#{name}" : "email/#{name}" + end + +end diff --git a/app/jobs/onceoff/clean_up_sidekiq_statistic.rb b/app/jobs/onceoff/clean_up_sidekiq_statistic.rb new file mode 100644 index 0000000000..69614a8827 --- /dev/null +++ b/app/jobs/onceoff/clean_up_sidekiq_statistic.rb @@ -0,0 +1,7 @@ +module Jobs + class CleanUpSidekiqStatistic < Jobs::Onceoff + def execute_onceoff(args) + $redis.without_namespace.del('sidekiq:sidekiq:statistic') + end + end +end diff --git a/app/jobs/onceoff/grand_first_reply_by_email.rb b/app/jobs/onceoff/grant_first_reply_by_email.rb similarity index 100% rename from app/jobs/onceoff/grand_first_reply_by_email.rb rename to app/jobs/onceoff/grant_first_reply_by_email.rb diff --git a/app/jobs/regular/process_email.rb b/app/jobs/regular/process_email.rb index a01e3bc07d..81acb4f8af 100644 --- a/app/jobs/regular/process_email.rb +++ b/app/jobs/regular/process_email.rb @@ -4,7 +4,7 @@ module Jobs sidekiq_options retry: 3 def execute(args) - Email::Processor.process!(args[:mail], false) + Email::Processor.process!(args[:mail], args[:retry_on_rate_limit] || false) end sidekiq_retries_exhausted do |msg| diff --git a/app/jobs/regular/publish_topic_to_category.rb b/app/jobs/regular/publish_topic_to_category.rb index 11424dd32f..c67adde491 100644 --- a/app/jobs/regular/publish_topic_to_category.rb +++ b/app/jobs/regular/publish_topic_to_category.rb @@ -8,8 +8,15 @@ module Jobs return if topic.blank? PostTimestampChanger.new(timestamp: Time.zone.now, topic: topic).change! do - topic.change_category_to_id(topic_status_update.category_id) + if topic.private_message? + topic = TopicConverter.new(topic, Discourse.system_user) + .convert_to_public_topic(topic_status_update.category_id) + else + topic.change_category_to_id(topic_status_update.category_id) + end + topic.update_columns(visible: true) + topic_status_update.trash!(Discourse.system_user) end MessageBus.publish("/topic/#{topic.id}", reload_topic: true, refresh_stream: true) diff --git a/app/jobs/scheduled/clean_up_unused_staged_users.rb b/app/jobs/scheduled/clean_up_unused_staged_users.rb index f11ec67c1e..074b09e978 100644 --- a/app/jobs/scheduled/clean_up_unused_staged_users.rb +++ b/app/jobs/scheduled/clean_up_unused_staged_users.rb @@ -6,11 +6,21 @@ module Jobs def execute(args) destroyer = UserDestroyer.new(Discourse.system_user) - User.joins(:user_stat) - .where(staged: true) - .where("users.created_at < ?", 1.year.ago) - .where("user_stats.post_count = 0") - .find_each { |user| destroyer.destroy(user) } + User.joins("LEFT JOIN posts ON posts.user_id = users.id") + .where("posts.user_id IS NULL") + .where(staged: true) + .where("users.created_at < ?", 1.year.ago) + .find_each do |user| + + begin + destroyer.destroy(user) + rescue => e + Discourse.handle_job_exception(e, + message: "Cleaning up unused staged user", + extra: { user_id: user.id } + ) + end + end end end diff --git a/app/jobs/scheduled/enqueue_digest_emails.rb b/app/jobs/scheduled/enqueue_digest_emails.rb index bab41fb142..1bd4ed4fdf 100644 --- a/app/jobs/scheduled/enqueue_digest_emails.rb +++ b/app/jobs/scheduled/enqueue_digest_emails.rb @@ -5,10 +5,9 @@ module Jobs every 30.minutes def execute(args) - unless SiteSetting.disable_digest_emails? - target_user_ids.each do |user_id| - Jobs.enqueue(:user_email, type: :digest, user_id: user_id) - end + return if SiteSetting.disable_digest_emails? || SiteSetting.private_email? + target_user_ids.each do |user_id| + Jobs.enqueue(:user_email, type: :digest, user_id: user_id) end end diff --git a/app/jobs/scheduled/pending_flags_reminder.rb b/app/jobs/scheduled/pending_flags_reminder.rb index 94fb480ef1..895c6c2492 100644 --- a/app/jobs/scheduled/pending_flags_reminder.rb +++ b/app/jobs/scheduled/pending_flags_reminder.rb @@ -4,12 +4,15 @@ module Jobs class PendingFlagsReminder < Jobs::Scheduled - every 1.day + every 1.hour def execute(args) if SiteSetting.notify_about_flags_after > 0 && - PostAction.flagged_posts_count > 0 && - FlagQuery.flagged_post_actions('active').where('post_actions.created_at < ?', SiteSetting.notify_about_flags_after.to_i.hours.ago).pluck(:id).count > 0 + PostAction.flagged_posts_count > 0 && + flag_ids.size > 0 && last_notified_id.to_i < flag_ids.max + + mentions = active_moderator_usernames.size > 0 ? + "@#{active_moderator_usernames.join(', @')} " : "" PostCreator.create( Discourse.system_user, @@ -17,9 +20,40 @@ module Jobs archetype: Archetype.private_message, subtype: TopicSubtype.system_message, title: I18n.t('flags_reminder.subject_template', { count: PostAction.flagged_posts_count }), - raw: I18n.t('flags_reminder.flags_were_submitted', { count: SiteSetting.notify_about_flags_after }) + raw: mentions + I18n.t('flags_reminder.flags_were_submitted', { count: SiteSetting.notify_about_flags_after }) ) + + self.last_notified_id = flag_ids.max end + + true + end + + def flag_ids + @_flag_ids ||= FlagQuery.flagged_post_actions('active') + .where('post_actions.created_at < ?', SiteSetting.notify_about_flags_after.to_i.hours.ago) + .pluck(:id) + end + + def last_notified_id + $redis.get(self.class.last_notified_key)&.to_i + end + + def last_notified_id=(arg) + $redis.set(self.class.last_notified_key, arg) + end + + def self.last_notified_key + "last_notified_pending_flag_id" + end + + def active_moderator_usernames + @_active_moderator_usernames ||= + User.where(moderator: true) + .human_users + .order('last_seen_at DESC') + .limit(3) + .pluck(:username) end end diff --git a/app/mailers/invite_mailer.rb b/app/mailers/invite_mailer.rb index 346507326f..3614123716 100644 --- a/app/mailers/invite_mailer.rb +++ b/app/mailers/invite_mailer.rb @@ -5,6 +5,7 @@ class InviteMailer < ActionMailer::Base class UserNotificationRenderer < ActionView::Base include UserNotificationsHelper + include EmailHelper end def send_invite(invite, custom_message=nil) @@ -30,12 +31,18 @@ class InviteMailer < ActionMailer::Base template = 'custom_invite_mailer' end + topic_title = first_topic.try(:title) + if SiteSetting.private_email? + topic_title = I18n.t("system_messages.private_topic_title", id: first_topic.id) + topic_excerpt = "" + end + build_email(invite.email, template: template, invitee_name: invitee_name, site_domain_name: Discourse.current_hostname, invite_link: "#{Discourse.base_url}/invites/#{invite.invite_key}", - topic_title: first_topic.try(:title), + topic_title: topic_title, topic_excerpt: topic_excerpt, site_description: SiteSetting.site_description, site_title: SiteSetting.title, diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index acf82ae2dc..dff3331aa0 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -5,7 +5,7 @@ require_dependency 'age_words' class UserNotifications < ActionMailer::Base include UserNotificationsHelper include ApplicationHelper - helper :application + helper :application, :email default charset: 'UTF-8' include Email::BuildEmailHelper @@ -118,7 +118,7 @@ class UserNotifications < ActionMailer::Base .for_mailing_list(user, min_date) .where('posts.post_type = ?', Post.types[:regular]) .where('posts.deleted_at IS NULL AND posts.hidden = false AND posts.user_deleted = false') - .where("posts.post_number > ? AND posts.score > ?", 1, ScoreCalculator.default_score_weights[:like_score] * 1.0) + .where("posts.post_number > ? AND posts.score > ?", 1, ScoreCalculator.default_score_weights[:like_score] * 5.0) .limit(SiteSetting.digest_posts) else [] @@ -284,10 +284,12 @@ class UserNotifications < ActionMailer::Base class UserNotificationRenderer < ActionView::Base include UserNotificationsHelper + include EmailHelper end def self.get_context_posts(post, topic_user, user) - if user.user_option.email_previous_replies == UserOption.previous_replies_type[:never] + if (user.user_option.email_previous_replies == UserOption.previous_replies_type[:never]) || + SiteSetting.private_email? return [] end @@ -349,6 +351,7 @@ class UserNotifications < ActionMailer::Base def send_notification_email(opts) post = opts[:post] title = opts[:title] + allow_reply_by_email = opts[:allow_reply_by_email] use_site_subject = opts[:use_site_subject] add_re_to_subject = opts[:add_re_to_subject] && post.post_number > 1 @@ -377,6 +380,10 @@ class UserNotifications < ActionMailer::Base show_category_in_subject = nil end + if SiteSetting.private_email? + title = I18n.t("system_messages.private_topic_title", id: post.topic_id) + end + context = "" tu = TopicUser.get(post.topic_id, user) context_posts = self.class.get_context_posts(post, tu, user) @@ -400,6 +407,7 @@ class UserNotifications < ActionMailer::Base invite_template = "user_notifications.invited_to_topic_body" end topic_excerpt = post.excerpt.tr("\n", " ") if post.is_first_post? && post.excerpt + topic_excerpt = "" if SiteSetting.private_email? message = I18n.t(invite_template, username: username, topic_title: gsub_emoji_to_unicode(title), topic_excerpt: topic_excerpt, site_title: SiteSetting.title, site_description: SiteSetting.site_description) unless translation_override_exists @@ -418,7 +426,12 @@ class UserNotifications < ActionMailer::Base .count) >= (SiteSetting.max_emails_per_day_per_user-1) in_reply_to_post = post.reply_to_post if user.user_option.email_in_reply_to - message = email_post_markdown(post) + (reached_limit ? "\n\n#{I18n.t "user_notifications.reached_limit", count: SiteSetting.max_emails_per_day_per_user}" : ""); + if SiteSetting.private_email? + message = I18n.t('system_messages.contents_hidden') + else + message = email_post_markdown(post) + (reached_limit ? "\n\n#{I18n.t "user_notifications.reached_limit", count: SiteSetting.max_emails_per_day_per_user}" : ""); + end + unless translation_override_exists html = UserNotificationRenderer.new(Rails.configuration.paths["app/views"]).render( @@ -438,7 +451,7 @@ class UserNotifications < ActionMailer::Base topic_title: gsub_emoji_to_unicode(title), topic_title_url_encoded: title ? URI.encode(title) : title, message: message, - url: post.url, + url: post.url(without_slug: SiteSetting.private_email?), post_id: post.id, topic_id: post.topic_id, context: context, diff --git a/app/models/badge.rb b/app/models/badge.rb index 5ffde4726b..25b2a38105 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -132,12 +132,24 @@ class Badge < ActiveRecord::Base exec_sql <<-SQL.squish DELETE FROM user_badges USING user_badges ub - LEFT JOIN users u ON u.id = ub.user_id - WHERE u.id IS NULL - AND user_badges.id = ub.id + LEFT JOIN users u ON u.id = ub.user_id + WHERE u.id IS NULL + AND user_badges.id = ub.id SQL - Badge.find_each(&:reset_grant_count!) + exec_sql <<-SQL.squish + WITH X AS ( + SELECT badge_id + , COUNT(user_id) users + FROM user_badges + GROUP BY badge_id + ) + UPDATE badges + SET grant_count = X.users + FROM X + WHERE id = X.badge_id + AND grant_count <> X.users + SQL end def awarded_for_trust_level? diff --git a/app/models/category.rb b/app/models/category.rb index 2a48c6a53a..0a50c69d7d 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,5 +1,4 @@ require_dependency 'distributed_cache' -require_dependency 'sass/discourse_stylesheets' class Category < ActiveRecord::Base @@ -389,8 +388,8 @@ SQL group = group.id if group.is_a?(Group) # subtle, using Group[] ensures the group exists in the DB - group = Group[group.to_sym].id unless group.is_a?(Fixnum) - permission = CategoryGroup.permission_types[permission] unless permission.is_a?(Fixnum) + group = Group[group.to_sym].id unless group.is_a?(Integer) + permission = CategoryGroup.permission_types[permission] unless permission.is_a?(Integer) [group, permission] end @@ -492,7 +491,7 @@ SQL end def publish_discourse_stylesheet - DiscourseStylesheets.cache.clear + Stylesheet::Manager.cache.clear end def index_search diff --git a/app/models/category_featured_user.rb b/app/models/category_featured_user.rb index db524db0b8..fb75cca87b 100644 --- a/app/models/category_featured_user.rb +++ b/app/models/category_featured_user.rb @@ -8,7 +8,7 @@ class CategoryFeaturedUser < ActiveRecord::Base def self.feature_users_in(category_or_category_id) category_id = - if category_or_category_id.is_a?(Fixnum) + if category_or_category_id.is_a?(Integer) category_or_category_id else category_or_category_id.id diff --git a/app/models/child_theme.rb b/app/models/child_theme.rb new file mode 100644 index 0000000000..6e101bd8aa --- /dev/null +++ b/app/models/child_theme.rb @@ -0,0 +1,20 @@ +class ChildTheme < ActiveRecord::Base + belongs_to :parent_theme, class_name: 'Theme' + belongs_to :child_theme, class_name: 'Theme' +end + +# == Schema Information +# +# Table name: child_themes +# +# id :integer not null, primary key +# parent_theme_id :integer +# child_theme_id :integer +# created_at :datetime +# updated_at :datetime +# +# Indexes +# +# index_child_themes_on_child_theme_id_and_parent_theme_id (child_theme_id,parent_theme_id) UNIQUE +# index_child_themes_on_parent_theme_id_and_child_theme_id (parent_theme_id,child_theme_id) UNIQUE +# diff --git a/app/models/color_scheme.rb b/app/models/color_scheme.rb index d9c9216c27..6701f4cdf6 100644 --- a/app/models/color_scheme.rb +++ b/app/models/color_scheme.rb @@ -1,32 +1,36 @@ -require_dependency 'sass/discourse_stylesheets' require_dependency 'distributed_cache' class ColorScheme < ActiveRecord::Base - def self.themes + CUSTOM_SCHEMES = { + dark: { + "primary" => 'dddddd', + "secondary" => '222222', + "tertiary" => '0f82af', + "quaternary" => 'c14924', + "header_background" => '111111', + "header_primary" => '333333', + "highlight" => 'a87137', + "danger" => 'e45735', + "success" => '1ca551', + "love" => 'fa6c8d' + } + } + + def self.base_color_scheme_colors base_with_hash = {} base_colors.each do |name, color| - base_with_hash[name] = "##{color}" + base_with_hash[name] = "#{color}" end - [ - { id: 'default', colors: base_with_hash }, - { - id: 'dark', - colors: { - "primary" => '#dddddd', - "secondary" => '#222222', - "tertiary" => '#0f82af', - "quaternary" => '#c14924', - "header_background" => '#111111', - "header_primary" => '#333333', - "highlight" => '#a87137', - "danger" => '#e45735', - "success" => '#1ca551', - "love" => '#fa6c8d' - } - } + list = [ + { id: 'default', colors: base_with_hash } ] + + CUSTOM_SCHEMES.each do |k,v| + list.push({id: k.to_s, colors: v}) + end + list end def self.hex_cache @@ -39,12 +43,16 @@ class ColorScheme < ActiveRecord::Base alias_method :colors, :color_scheme_colors - scope :current_version, ->{ where(versioned_id: nil) } + before_save do + if self.id + self.version += 1 + end + end - after_destroy :destroy_versions after_save :publish_discourse_stylesheet after_save :dump_hex_cache after_destroy :dump_hex_cache + belongs_to :theme validates_associated :color_scheme_colors @@ -64,13 +72,18 @@ class ColorScheme < ActiveRecord::Base @base_colors end - def self.enabled - current_version.find_by(enabled: true) + def self.base_color_schemes + base_color_scheme_colors.map do |hash| + scheme = new(name: I18n.t("color_schemes.#{hash[:id]}"), base_scheme_id: hash[:id]) + scheme.colors = hash[:colors].map{|k,v| {name: k.to_s, hex: v.sub("#","")}} + scheme.is_base = true + scheme + end end def self.base return @base_color_scheme if @base_color_scheme - @base_color_scheme = new(name: I18n.t('color_schemes.base_theme_name'), enabled: false) + @base_color_scheme = new(name: I18n.t('color_schemes.base_theme_name')) @base_color_scheme.colors = base_colors.map { |name, hex| {name: name, hex: hex} } @base_color_scheme.is_base = true @base_color_scheme @@ -101,7 +114,7 @@ class ColorScheme < ActiveRecord::Base end # Can't use `where` here because base doesn't allow it - (enabled || base).colors.find {|c| c.name == name }.try(:hex) || :nil + (base).colors.find {|c| c.name == name }.try(:hex) || :nil end def self.hex_for_name(name) @@ -129,17 +142,39 @@ class ColorScheme < ActiveRecord::Base end end - def previous_version - ColorScheme.where(versioned_id: self.id).where('version < ?', self.version).order('version DESC').first + def base_colors + colors = nil + if base_scheme_id && base_scheme_id != "default" + colors = CUSTOM_SCHEMES[base_scheme_id.to_sym] + end + colors || ColorScheme.base_colors end - def destroy_versions - ColorScheme.where(versioned_id: self.id).destroy_all + def resolved_colors + resolved = ColorScheme.base_colors.dup + if base_scheme_id && base_scheme_id != "default" + if scheme = CUSTOM_SCHEMES[base_scheme_id.to_sym] + scheme.each do |name, value| + resolved[name] = value + end + end + end + colors.each do |c| + resolved[c.name] = c.hex + end + resolved end def publish_discourse_stylesheet - MessageBus.publish("/discourse_stylesheet", self.name) - DiscourseStylesheets.cache.clear + if self.id + themes = Theme.where(color_scheme_id: self.id).to_a + if themes.present? + Stylesheet::Manager.cache.clear + themes.each do |theme| + theme.notify_scheme_change(_clear_manager_cache = false) + end + end + end end def dump_hex_cache @@ -152,13 +187,12 @@ end # # Table name: color_schemes # -# id :integer not null, primary key -# name :string not null -# enabled :boolean default(FALSE), not null -# versioned_id :integer -# version :integer default(1), not null -# created_at :datetime not null -# updated_at :datetime not null -# via_wizard :boolean default(FALSE), not null -# theme_id :string +# id :integer not null, primary key +# name :string not null +# version :integer default(1), not null +# created_at :datetime not null +# updated_at :datetime not null +# via_wizard :boolean default(FALSE), not null +# base_scheme_id :string +# theme_id :integer # diff --git a/app/models/draft_sequence.rb b/app/models/draft_sequence.rb index 0e1c44359e..af4eb647c9 100644 --- a/app/models/draft_sequence.rb +++ b/app/models/draft_sequence.rb @@ -1,7 +1,7 @@ class DraftSequence < ActiveRecord::Base def self.next!(user,key) user_id = user - user_id = user.id unless user.class == Fixnum + user_id = user.id unless user.is_a?(Integer) return 0 if user_id < 0 @@ -19,7 +19,7 @@ class DraftSequence < ActiveRecord::Base return nil unless user user_id = user - user_id = user.id unless user.class == Fixnum + user_id = user.id unless user.is_a?(Integer) # perf critical path r = exec_sql('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key).values diff --git a/app/models/embeddable_host.rb b/app/models/embeddable_host.rb index f94ce56b1b..1bcd29af02 100644 --- a/app/models/embeddable_host.rb +++ b/app/models/embeddable_host.rb @@ -39,7 +39,7 @@ class EmbeddableHost < ActiveRecord::Base private def host_must_be_valid - if host !~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,7}(:[0-9]{1,5})?(\/.*)?\Z/i && + if host !~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,10}(:[0-9]{1,5})?(\/.*)?\Z/i && host !~ /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(:[0-9]{1,5})?(\/.*)?\Z/ && host !~ /\A([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.)?localhost(\:[0-9]{1,5})?(\/.*)?\Z/i errors.add(:host, I18n.t('errors.messages.invalid')) diff --git a/app/models/group.rb b/app/models/group.rb index 74a4126b21..1c185cc791 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -171,16 +171,17 @@ class Group < ActiveRecord::Base unless group = self.lookup_group(name) group = Group.new(name: name.to_s, automatic: true) + group.default_notification_level = 2 if AUTO_GROUPS[:moderators] == id group.id = id group.save! end # don't allow shoddy localization to break this - localized_name = I18n.t("groups.default_names.#{name}") + localized_name = I18n.t("groups.default_names.#{name}").downcase validator = UsernameValidator.new(localized_name) group.name = - if !Group.where("lower(name) = ?", localized_name).exists? && validator.valid_format? + if !Group.where("LOWER(name) = ?", localized_name).exists? && validator.valid_format? localized_name else name @@ -195,54 +196,45 @@ class Group < ActiveRecord::Base end # Remove people from groups they don't belong in. - # - # BEWARE: any of these subqueries could match ALL the user records, - # so they can't be used in IN clauses. - remove_user_subquery = case name - when :admins - "SELECT u.id FROM users u WHERE NOT u.admin" - when :moderators - "SELECT u.id FROM users u WHERE NOT u.moderator" - when :staff - "SELECT u.id FROM users u WHERE NOT u.admin AND NOT u.moderator" - when :trust_level_0, :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4 - "SELECT u.id FROM users u WHERE u.trust_level < #{id - 10}" - end + remove_subquery = case name + when :admins + "SELECT id FROM users WHERE NOT admin" + when :moderators + "SELECT id FROM users WHERE NOT moderator" + when :staff + "SELECT id FROM users WHERE NOT admin AND NOT moderator" + when :trust_level_0, :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4 + "SELECT id FROM users WHERE trust_level < #{id - 10}" + end - remove_ids = exec_sql("SELECT gu.id id - FROM group_users gu, - (#{remove_user_subquery}) u - WHERE gu.group_id = #{group.id} - AND gu.user_id = u.id").map {|x| x['id']} - - if remove_ids.length > 0 - remove_ids.each_slice(100) do |ids| - GroupUser.where(id: ids).delete_all - end - end + exec_sql <<-SQL + DELETE FROM group_users + USING (#{remove_subquery}) X + WHERE group_id = #{group.id} + AND user_id = X.id + SQL # Add people to groups - real_ids = case name - when :admins - "SELECT u.id FROM users u WHERE u.admin" - when :moderators - "SELECT u.id FROM users u WHERE u.moderator" - when :staff - "SELECT u.id FROM users u WHERE u.moderator OR u.admin" - when :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4 - "SELECT u.id FROM users u WHERE u.trust_level >= #{id-10}" - when :trust_level_0 - "SELECT u.id FROM users u" - end + insert_subquery = case name + when :admins + "SELECT id FROM users WHERE admin" + when :moderators + "SELECT id FROM users WHERE moderator" + when :staff + "SELECT id FROM users WHERE moderator OR admin" + when :trust_level_1, :trust_level_2, :trust_level_3, :trust_level_4 + "SELECT id FROM users WHERE trust_level >= #{id - 10}" + when :trust_level_0 + "SELECT id FROM users" + end - missing_users = GroupUser - .joins("RIGHT JOIN (#{real_ids}) X ON X.id = user_id AND group_id = #{group.id}") - .where("user_id IS NULL") - .select("X.id") - - missing_users.each do |u| - group.group_users.build(user_id: u.id) - end + exec_sql <<-SQL + INSERT INTO group_users (group_id, user_id, created_at, updated_at) + SELECT #{group.id}, X.id, now(), now() + FROM group_users + RIGHT JOIN (#{insert_subquery}) X ON X.id = user_id AND group_id = #{group.id} + WHERE user_id IS NULL + SQL group.save! @@ -258,18 +250,24 @@ class Group < ActiveRecord::Base end def self.reset_all_counters! - Group.pluck(:id).each do |group_id| - Group.reset_counters(group_id, :group_users) - end + exec_sql <<-SQL + WITH X AS ( + SELECT group_id + , COUNT(user_id) users + FROM group_users + GROUP BY group_id + ) + UPDATE groups + SET user_count = X.users + FROM X + WHERE id = X.group_id + AND user_count <> X.users + SQL end def self.refresh_automatic_groups!(*args) - if args.length == 0 - args = AUTO_GROUPS.keys - end - args.each do |group| - refresh_automatic_group!(group) - end + args = AUTO_GROUPS.keys if args.empty? + args.each { |group| refresh_automatic_group!(group) } end def self.ensure_automatic_groups! @@ -465,24 +463,15 @@ class Group < ActiveRecord::Base return if new_record? && !self.title.present? if self.title_changed? - sql = < COALESCE(:title,'') AND - id IN ( - SELECT user_id - FROM group_users - WHERE group_id = :id - ) -SQL + sql = <<-SQL.squish + UPDATE users + SET title = :title + WHERE (title = :title_was OR title = '' OR title IS NULL) + AND COALESCE(title,'') <> COALESCE(:title,'') + AND id IN (SELECT user_id FROM group_users WHERE group_id = :id) + SQL - self.class.exec_sql(sql, - title: title, - title_was: title_was, - id: id - ) + self.class.exec_sql(sql, title: title, title_was: title_was, id: id) end end @@ -552,6 +541,7 @@ end # public :boolean default(FALSE), not null # allow_membership_requests :boolean default(FALSE), not null # full_name :string +# default_notification_level :integer default(3), not null # # Indexes # diff --git a/app/models/group_user.rb b/app/models/group_user.rb index 8d1e3beba5..b559580390 100644 --- a/app/models/group_user.rb +++ b/app/models/group_user.rb @@ -10,6 +10,7 @@ class GroupUser < ActiveRecord::Base after_save :set_primary_group after_destroy :remove_primary_group + before_create :set_notification_level after_save :grant_trust_level def self.notification_levels @@ -18,6 +19,10 @@ class GroupUser < ActiveRecord::Base protected + def set_notification_level + self.notification_level = group&.default_notification_level || 3 + end + def set_primary_group if group.primary_group self.class.exec_sql("UPDATE users diff --git a/app/models/invite.rb b/app/models/invite.rb index 15e5fcccc3..5fd7471f00 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -116,6 +116,8 @@ class Invite < ActiveRecord::Base invite = nil end + invite.update_columns(created_at: Time.zone.now, updated_at: Time.zone.now) if invite + if !invite create_args = { invited_by: invited_by, email: lower_email } create_args[:moderator] = true if opts[:moderator] diff --git a/app/models/invite_redeemer.rb b/app/models/invite_redeemer.rb index 6f1406b06a..a6212f9169 100644 --- a/app/models/invite_redeemer.rb +++ b/app/models/invite_redeemer.rb @@ -61,8 +61,6 @@ InviteRedeemer = Struct.new(:invite, :username, :name, :password) do add_user_to_groups send_welcome_message notify_invitee - send_password_instructions - enqueue_activation_mail delete_duplicate_invites end @@ -121,19 +119,6 @@ InviteRedeemer = Struct.new(:invite, :username, :name, :password) do end end - def send_password_instructions - if !SiteSetting.enable_sso && SiteSetting.enable_local_logins && !invited_user.has_password? - Jobs.enqueue(:invite_password_instructions_email, username: invited_user.username) - end - end - - def enqueue_activation_mail - if invited_user.has_password? - email_token = invited_user.email_tokens.create(email: invited_user.email) - Jobs.enqueue(:critical_user_email, type: :signup, user_id: invited_user.id, email_token: email_token.token) - end - end - def notify_invitee if inviter = invite.invited_by inviter.notifications.create(notification_type: Notification.types[:invitee_accepted], diff --git a/app/models/notification.rb b/app/models/notification.rb index 96e3e43f9e..a09451e4ee 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -19,14 +19,20 @@ class Notification < ActiveRecord::Base after_commit :refresh_notification_count, on: [:create, :update] def self.ensure_consistency! - Notification.exec_sql(" - DELETE FROM Notifications n WHERE notification_type = :id AND - NOT EXISTS( - SELECT 1 FROM posts p - JOIN topics t ON t.id = p.topic_id - WHERE p.deleted_at is null AND t.deleted_at IS NULL - AND p.post_number = n.post_number AND t.id = n.topic_id - )" , id: Notification.types[:private_message]) + Notification.exec_sql <<-SQL + DELETE + FROM notifications n + WHERE notification_type = #{Notification.types[:private_message]} + AND NOT EXISTS ( + SELECT 1 + FROM posts p + JOIN topics t ON t.id = p.topic_id + WHERE p.deleted_at IS NULL + AND t.deleted_at IS NULL + AND p.post_number = n.post_number + AND t.id = n.topic_id + ) + SQL end def self.types @@ -66,13 +72,12 @@ class Notification < ActiveRecord::Base end def self.read(user, notification_ids) - count = Notification.where(user_id: user.id, - id: notification_ids, - read: false).update_all(read: true) + count = Notification.where(user_id: user.id) + .where(id: notification_ids) + .where(read: false) + .update_all(read: true) - if count > 0 - user.publish_notifications_state - end + user.publish_notifications_state if count > 0 end def self.interesting_after(min_date) diff --git a/app/models/notification_level_when_replying_site_setting.rb b/app/models/notification_level_when_replying_site_setting.rb index 66b07cb545..296c7c066f 100644 --- a/app/models/notification_level_when_replying_site_setting.rb +++ b/app/models/notification_level_when_replying_site_setting.rb @@ -15,7 +15,8 @@ class NotificationLevelWhenReplyingSiteSetting < EnumSiteSetting def self.values @values ||= [ { name: 'topic.notifications.watching.title', value: notification_levels[:watching] }, - { name: 'topic.notifications.tracking.title', value: notification_levels[:tracking] } + { name: 'topic.notifications.tracking.title', value: notification_levels[:tracking] }, + { name: 'topic.notifications.regular.title', value: notification_levels[:regular] } ] end diff --git a/app/models/plugin_store.rb b/app/models/plugin_store.rb index ecf8d9c73e..5a7675344e 100644 --- a/app/models/plugin_store.rb +++ b/app/models/plugin_store.rb @@ -42,7 +42,7 @@ class PluginStore def self.cast_value(type, value) case type - when "Fixnum" then value.to_i + when "Integer", "Fixnum" then value.to_i when "TrueClass", "FalseClass" then value == "true" when "JSON" then map_json(::JSON.parse(value)) else value diff --git a/app/models/post.rb b/app/models/post.rb index 2ed6d3e065..13d88aec1b 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -400,9 +400,11 @@ class Post < ActiveRecord::Base "#{Discourse.base_url}#{url}" end - def url + def url(opts=nil) + opts ||= {} + if topic - Post.url(topic.slug, topic.id, post_number) + Post.url(topic.slug, topic.id, post_number, opts) else "/404" end @@ -412,8 +414,13 @@ class Post < ActiveRecord::Base "#{Discourse.base_url}/email/unsubscribe/#{UnsubscribeKey.create_key_for(user, self)}" end - def self.url(slug, topic_id, post_number) - "/t/#{slug}/#{topic_id}/#{post_number}" + def self.url(slug, topic_id, post_number, opts=nil) + opts ||= {} + + result = "/t/" + result << "#{slug}/" unless !!opts[:without_slug] + + "#{result}#{topic_id}/#{post_number}" end def self.urls(post_ids) diff --git a/app/models/post_mover.rb b/app/models/post_mover.rb index 793bfcc59a..9dff1988fb 100644 --- a/app/models/post_mover.rb +++ b/app/models/post_mover.rb @@ -77,7 +77,7 @@ class PostMover post.is_first_post? ? create_first_post(post) : move(post) end - PostReply.where("reply_id in (:post_ids) OR post_id in (:post_ids)", post_ids: post_ids).each do |post_reply| + PostReply.where("reply_id IN (:post_ids) OR post_id IN (:post_ids)", post_ids: post_ids).each do |post_reply| if post_reply.post && post_reply.reply && post_reply.reply.topic_id != post_reply.post.topic_id PostReply.delete_all(reply_id: post_reply.reply.id, post_id: post_reply.post.id) end diff --git a/app/models/remote_theme.rb b/app/models/remote_theme.rb new file mode 100644 index 0000000000..d32a7705af --- /dev/null +++ b/app/models/remote_theme.rb @@ -0,0 +1,129 @@ +require_dependency 'git_importer' + +class RemoteTheme < ActiveRecord::Base + has_one :theme + + def self.import_theme(url, user=Discourse.system_user) + importer = GitImporter.new(url) + importer.import! + + theme_info = JSON.parse(importer["about.json"]) + theme = Theme.new(user_id: user&.id || -1, name: theme_info["name"]) + + remote_theme = new + theme.remote_theme = remote_theme + + remote_theme.remote_url = importer.url + remote_theme.update_from_remote(importer) + + theme.save! + theme + ensure + begin + importer.cleanup! + rescue => e + Rails.logger.warn("Failed cleanup remote git #{e}") + end + end + + def update_remote_version + importer = GitImporter.new(remote_url) + importer.import! + self.updated_at = Time.zone.now + self.remote_version, self.commits_behind = importer.commits_since(remote_version) + end + + def update_from_remote(importer=nil) + return unless remote_url + cleanup = false + + unless importer + cleanup = true + importer = GitImporter.new(remote_url) + importer.import! + end + + Theme.targets.keys.each do |target| + Theme::ALLOWED_FIELDS.each do |field| + lookup = + if field == "scss" + "#{target}.scss" + elsif field == "embedded_scss" && target == :common + "embedded.scss" + else + "#{field}.html" + end + + value = importer["#{target}/#{lookup}"] + theme.set_field(target.to_sym, field, value) + end + end + + theme_info = JSON.parse(importer["about.json"]) + self.license_url ||= theme_info["license_url"] + self.about_url ||= theme_info["about_url"] + self.remote_updated_at = Time.zone.now + self.remote_version = importer.version + self.local_version = importer.version + self.commits_behind = 0 + + update_theme_color_schemes(theme, theme_info["color_schemes"]) + + self + ensure + begin + importer.cleanup! if cleanup + rescue => e + Rails.logger.warn("Failed cleanup remote git #{e}") + end + end + + def normalize_override(hex) + return unless hex + + override = hex.downcase + if override !~ /\A[0-9a-f]{6}\z/ + override = nil + end + override + end + + def update_theme_color_schemes(theme, schemes) + return if schemes.blank? + + schemes.each do |name, colors| + existing = theme.color_schemes.find_by(name: name) + if existing + existing.colors.each do |c| + override = normalize_override(colors[c.name]) + if override && c.hex != override + c.hex = override + theme.notify_color_change(c) + end + end + else + scheme = theme.color_schemes.build(name: name) + ColorScheme.base.colors_hashes.each do |color| + override = normalize_override(colors[color[:name]]) + scheme.color_scheme_colors << ColorSchemeColor.new(name: color[:name], hex: override || color[:hex]) + end + end + end + end +end + +# == Schema Information +# +# Table name: remote_themes +# +# id :integer not null, primary key +# remote_url :string not null +# remote_version :string +# local_version :string +# about_url :string +# license_url :string +# commits_behind :integer +# remote_updated_at :datetime +# created_at :datetime +# updated_at :datetime +# diff --git a/app/models/report.rb b/app/models/report.rb index b568aec1ea..76c162ffb1 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -82,7 +82,6 @@ class Report .sum(:count) end - def self.report_visits(report) basic_report_about report, UserVisit, :by_day, report.start_date, report.end_date, report.group_id diff --git a/app/models/s3_region_site_setting.rb b/app/models/s3_region_site_setting.rb index 14fbea51d4..153f92171b 100644 --- a/app/models/s3_region_site_setting.rb +++ b/app/models/s3_region_site_setting.rb @@ -15,6 +15,7 @@ class S3RegionSiteSetting < EnumSiteSetting 'us-west-2', 'us-gov-west-1', 'eu-west-1', + 'eu-west-2', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', diff --git a/app/models/scheduler_stat.rb b/app/models/scheduler_stat.rb index 55dc8fda6a..258a437f64 100644 --- a/app/models/scheduler_stat.rb +++ b/app/models/scheduler_stat.rb @@ -17,4 +17,5 @@ end # live_slots_finish :integer # started_at :datetime not null # success :boolean +# error :text # diff --git a/app/models/site_customization.rb b/app/models/site_customization.rb deleted file mode 100644 index 4b2e0ab988..0000000000 --- a/app/models/site_customization.rb +++ /dev/null @@ -1,299 +0,0 @@ -require_dependency 'sass/discourse_sass_compiler' -require_dependency 'sass/discourse_stylesheets' -require_dependency 'distributed_cache' - -class SiteCustomization < ActiveRecord::Base - ENABLED_KEY = '7e202ef2-56d7-47d5-98d8-a9c8d15e57dd' - - COMPILER_VERSION = 4 - - @cache = DistributedCache.new('site_customization') - - def self.css_fields - %w(stylesheet mobile_stylesheet embedded_css) - end - - def self.html_fields - %w(body_tag head_tag header mobile_header footer mobile_footer) - end - - before_create do - self.enabled ||= false - self.key ||= SecureRandom.uuid - true - end - - def compile_stylesheet(scss) - DiscourseSassCompiler.compile("@import \"theme_variables\";\n" << scss, 'custom') - rescue => e - puts e.backtrace.join("\n") unless Sass::SyntaxError === e - raise e - end - - def transpile(es6_source, version) - template = Tilt::ES6ModuleTranspilerTemplate.new {} - wrapped = < { - #{es6_source} -}); -PLUGIN_API_JS - - template.babel_transpile(wrapped) - end - - def process_html(html) - doc = Nokogiri::HTML.fragment(html) - doc.css('script[type="text/x-handlebars"]').each do |node| - name = node["name"] || node["data-template-name"] || "broken" - is_raw = name =~ /\.raw$/ - if is_raw - template = "require('discourse-common/lib/raw-handlebars').template(#{Barber::Precompiler.compile(node.inner_html)})" - node.replace < - (function() { - Discourse.RAW_TEMPLATES[#{name.sub(/\.raw$/, '').inspect}] = #{template}; - })(); - -COMPILED - else - template = "Ember.HTMLBars.template(#{Barber::Ember::Precompiler.compile(node.inner_html)})" - node.replace < - (function() { - Ember.TEMPLATES[#{name.inspect}] = #{template}; - })(); - -COMPILED - end - - end - - doc.css('script[type="text/discourse-plugin"]').each do |node| - if node['version'].present? - begin - code = transpile(node.inner_html, node['version']) - node.replace("") - rescue MiniRacer::RuntimeError => ex - node.replace("") - end - end - end - - doc.to_s - end - - before_save do - SiteCustomization.html_fields.each do |html_attr| - if self.send("#{html_attr}_changed?") - self.send("#{html_attr}_baked=", process_html(self.send(html_attr))) - end - end - - SiteCustomization.css_fields.each do |stylesheet_attr| - if self.send("#{stylesheet_attr}_changed?") - begin - self.send("#{stylesheet_attr}_baked=", compile_stylesheet(self.send(stylesheet_attr))) - rescue Sass::SyntaxError => e - self.send("#{stylesheet_attr}_baked=", DiscourseSassCompiler.error_as_css(e, "custom stylesheet")) - end - end - end - end - - def any_stylesheet_changed? - SiteCustomization.css_fields.each do |fieldname| - return true if self.send("#{fieldname}_changed?") - end - false - end - - after_save do - remove_from_cache! - if any_stylesheet_changed? - MessageBus.publish "/file-change/#{key}", SecureRandom.hex - MessageBus.publish "/file-change/#{SiteCustomization::ENABLED_KEY}", SecureRandom.hex - end - MessageBus.publish "/header-change/#{key}", header if header_changed? - MessageBus.publish "/footer-change/#{key}", footer if footer_changed? - DiscourseStylesheets.cache.clear - end - - after_destroy do - remove_from_cache! - end - - def self.enabled_key - ENABLED_KEY.dup << RailsMultisite::ConnectionManagement.current_db - end - - def self.field_for_target(target=nil) - target ||= :desktop - - case target.to_sym - when :mobile then :mobile_stylesheet - when :desktop then :stylesheet - when :embedded then :embedded_css - end - end - - def self.baked_for_target(target=nil) - "#{field_for_target(target)}_baked".to_sym - end - - def self.enabled_stylesheet_contents(target=:desktop) - @cache["enabled_stylesheet_#{target}:#{COMPILER_VERSION}"] ||= where(enabled: true) - .order(:name) - .pluck(baked_for_target(target)) - .compact - .join("\n") - end - - def self.stylesheet_contents(key, target) - if key == ENABLED_KEY - enabled_stylesheet_contents(target) - else - where(key: key) - .pluck(baked_for_target(target)) - .first - end - end - - def self.custom_stylesheet(preview_style=nil, target=:desktop) - preview_style ||= ENABLED_KEY - if preview_style == ENABLED_KEY - stylesheet_link_tag(ENABLED_KEY, target, enabled_stylesheet_contents(target)) - else - lookup_field(preview_style, target, :stylesheet_link_tag) - end - end - - %i{header top footer head_tag body_tag}.each do |name| - define_singleton_method("custom_#{name}") do |preview_style=nil, target=:desktop| - preview_style ||= ENABLED_KEY - lookup_field(preview_style, target, name) - end - end - - def self.lookup_field(key, target, field) - return if key.blank? - - cache_key = "#{key}:#{target}:#{field}:#{COMPILER_VERSION}" - - lookup = @cache[cache_key] - return lookup.html_safe if lookup - - styles = if key == ENABLED_KEY - order(:name).where(enabled:true).to_a - else - [find_by(key: key)].compact - end - - val = if styles.present? - styles.map do |style| - lookup = target == :mobile ? "mobile_#{field}" : field - if html_fields.include?(lookup.to_s) - style.ensure_baked!(lookup) - style.send("#{lookup}_baked") - else - style.send(lookup) - end - end.compact.join("\n") - end - - (@cache[cache_key] = val || "").html_safe - end - - def self.remove_from_cache!(key, broadcast = true) - MessageBus.publish('/site_customization', key: key) if broadcast - clear_cache! - end - - def self.clear_cache! - @cache.clear - end - - def ensure_baked!(field) - - # If the version number changes, clear out all the baked fields - if compiler_version != COMPILER_VERSION - updates = { compiler_version: COMPILER_VERSION } - SiteCustomization.html_fields.each do |f| - updates["#{f}_baked".to_sym] = nil - end - - update_columns(updates) - end - - baked = send("#{field}_baked") - if baked.blank? - if val = self.send(field) - val = process_html(val) rescue "" - self.update_columns("#{field}_baked" => val) - end - end - end - - def remove_from_cache! - self.class.remove_from_cache!(self.class.enabled_key) - self.class.remove_from_cache!(key) - end - - def mobile_stylesheet_link_tag - stylesheet_link_tag(:mobile) - end - - def stylesheet_link_tag(target=:desktop) - content = self.send(SiteCustomization.field_for_target(target)) - SiteCustomization.stylesheet_link_tag(key, target, content) - end - - def self.stylesheet_link_tag(key, target, content) - return "" unless content.present? - - hash = Digest::MD5.hexdigest(content) - link_css_tag "/site_customizations/#{key}.css?target=#{target}&v=#{hash}" - end - - def self.link_css_tag(href) - href = (GlobalSetting.cdn_url || "") + "#{GlobalSetting.relative_url_root}#{href}&__ws=#{Discourse.current_hostname}" - %Q{}.html_safe - end -end - -# == Schema Information -# -# Table name: site_customizations -# -# id :integer not null, primary key -# name :string not null -# stylesheet :text -# header :text -# user_id :integer not null -# enabled :boolean not null -# key :string not null -# created_at :datetime not null -# updated_at :datetime not null -# stylesheet_baked :text default(""), not null -# mobile_stylesheet :text -# mobile_header :text -# mobile_stylesheet_baked :text -# footer :text -# mobile_footer :text -# head_tag :text -# body_tag :text -# top :text -# mobile_top :text -# embedded_css :text -# embedded_css_baked :text -# head_tag_baked :text -# body_tag_baked :text -# header_baked :text -# mobile_header_baked :text -# footer_baked :text -# mobile_footer_baked :text -# compiler_version :integer default(0), not null -# -# Indexes -# -# index_site_customizations_on_key (key) -# diff --git a/app/models/stylesheet_cache.rb b/app/models/stylesheet_cache.rb index c9dfa86762..2c568072cb 100644 --- a/app/models/stylesheet_cache.rb +++ b/app/models/stylesheet_cache.rb @@ -3,11 +3,11 @@ class StylesheetCache < ActiveRecord::Base MAX_TO_KEEP = 50 - def self.add(target,digest,content) + def self.add(target,digest,content,source_map) return false if where(target: target, digest: digest).exists? - success = create(target: target, digest: digest, content: content) + success = create(target: target, digest: digest, content: content, source_map: source_map) count = StylesheetCache.count if count > MAX_TO_KEEP @@ -39,6 +39,8 @@ end # content :text not null # created_at :datetime # updated_at :datetime +# theme_id :integer default(-1), not null +# source_map :text # # Indexes # diff --git a/app/models/theme.rb b/app/models/theme.rb new file mode 100644 index 0000000000..1ea01b2f1e --- /dev/null +++ b/app/models/theme.rb @@ -0,0 +1,281 @@ +require_dependency 'distributed_cache' +require_dependency 'stylesheet/compiler' +require_dependency 'stylesheet/manager' + +class Theme < ActiveRecord::Base + + ALLOWED_FIELDS = %w{scss embedded_scss head_tag header after_header body_tag footer} + + @cache = DistributedCache.new('theme') + + belongs_to :color_scheme + has_many :theme_fields, dependent: :destroy + has_many :child_theme_relation, class_name: 'ChildTheme', foreign_key: 'parent_theme_id', dependent: :destroy + has_many :child_themes, through: :child_theme_relation, source: :child_theme + has_many :color_schemes + belongs_to :remote_theme + + before_create do + self.key ||= SecureRandom.uuid + true + end + + def notify_color_change(color) + changed_colors << color + end + + after_save do + changed_colors.each(&:save!) + changed_colors.clear + changed_fields.each(&:save!) + changed_fields.clear + + Theme.expire_site_cache! if user_selectable_changed? + + @dependant_themes = nil + @included_themes = nil + end + + after_save do + remove_from_cache! + notify_scheme_change if color_scheme_id_changed? + end + + after_destroy do + remove_from_cache! + if SiteSetting.default_theme_key == self.key + Theme.clear_default! + end + end + + after_commit ->(theme) do + theme.notify_theme_change + end, on: :update + + def self.theme_keys + if keys = @cache["theme_keys"] + return keys + end + @cache["theme_keys"] = Set.new(Theme.pluck(:key)) + end + + def self.user_theme_keys + if keys = @cache["user_theme_keys"] + return keys + end + @cache["theme_keys"] = Set.new( + Theme + .where('user_selectable OR key = ?', SiteSetting.default_theme_key) + .pluck(:key) + ) + end + + def self.expire_site_cache! + Site.clear_anon_cache! + ApplicationSerializer.expire_cache_fragment!("user_themes") + end + + def self.clear_default! + SiteSetting.default_theme_key = "" + expire_site_cache! + end + + def set_default! + SiteSetting.default_theme_key = key + Theme.expire_site_cache! + end + + def self.lookup_field(key, target, field) + return if key.blank? + + cache_key = "#{key}:#{target}:#{field}:#{ThemeField::COMPILER_VERSION}" + lookup = @cache[cache_key] + return lookup.html_safe if lookup + + target = target.to_sym + theme = find_by(key: key) + + val = theme.resolve_baked_field(target, field) if theme + + (@cache[cache_key] = val || "").html_safe + end + + def self.remove_from_cache!(themes=nil) + clear_cache! + end + + def self.clear_cache! + @cache.clear + end + + + def self.targets + @targets ||= Enum.new(common: 0, desktop: 1, mobile: 2) + end + + + def notify_scheme_change(clear_manager_cache=true) + Stylesheet::Manager.cache.clear if clear_manager_cache + message = refresh_message_for_targets(["desktop", "mobile", "admin"], self) + MessageBus.publish('/file-change', message) + end + + def notify_theme_change + Stylesheet::Manager.clear_theme_cache! + + themes = [self] + dependant_themes + + message = themes.map do |theme| + refresh_message_for_targets([:mobile_theme,:desktop_theme], theme) + end.compact.flatten + MessageBus.publish('/file-change', message) + end + + def refresh_message_for_targets(targets, theme) + targets.map do |target| + href = Stylesheet::Manager.stylesheet_href(target.to_sym, theme.key) + if href + { + target: target, + new_href: href, + theme_key: theme.key + } + end + end + end + + def dependant_themes + @dependant_themes ||= resolve_dependant_themes(:up) + end + + def included_themes + @included_themes ||= resolve_dependant_themes(:down) + end + + def resolve_dependant_themes(direction) + + select_field,where_field=nil + + if direction == :up + select_field = "parent_theme_id" + where_field = "child_theme_id" + elsif direction == :down + select_field = "child_theme_id" + where_field = "parent_theme_id" + else + raise "Unknown direction" + end + + themes = [] + return [] unless id + + uniq = Set.new + uniq << id + + iterations = 0 + added = [id] + + while added.length > 0 && iterations < 5 + + iterations += 1 + + new_themes = Theme.where("id in (SELECT #{select_field} + FROM child_themes + WHERE #{where_field} in (?))", added).to_a + + added = [] + new_themes.each do |theme| + unless uniq.include?(theme.id) + added << theme.id + uniq << theme.id + themes << theme + end + end + + end + + themes + end + + def resolve_baked_field(target, name) + list_baked_fields(target,name).map{|f| f.value_baked || f.value}.join("\n") + end + + def list_baked_fields(target, name) + + target = target.to_sym + + theme_ids = [self.id] + (included_themes.map(&:id) || []) + fields = ThemeField.where(target: [Theme.targets[target], Theme.targets[:common]]) + .where(name: name.to_s) + .includes(:theme) + .joins("JOIN ( + SELECT #{theme_ids.map.with_index{|id,idx| "#{id} AS theme_id, #{idx} AS sort_column"}.join(" UNION ALL SELECT ")} + ) as X ON X.theme_id = theme_fields.theme_id") + .order('sort_column, target') + fields.each(&:ensure_baked!) + fields + end + + def remove_from_cache! + self.class.remove_from_cache! + end + + def changed_fields + @changed_fields ||= [] + end + + def changed_colors + @changed_colors ||= [] + end + + def set_field(target, name, value) + name = name.to_s + + target_id = Theme.targets[target.to_sym] + raise "Unknown target #{target} passed to set field" unless target_id + + field = theme_fields.find{|f| f.name==name && f.target == target_id} + if field + if value.blank? + theme_fields.delete field.destroy + else + if field.value != value + field.value = value + changed_fields << field + end + end + else + theme_fields.build(target: target_id, value: value, name: name) if value.present? + end + end + + def add_child_theme!(theme) + child_theme_relation.create!(child_theme_id: theme.id) + @included_themes = nil + child_themes.reload + save! + end +end + +# == Schema Information +# +# Table name: themes +# +# id :integer not null, primary key +# name :string not null +# user_id :integer not null +# key :string not null +# created_at :datetime not null +# updated_at :datetime not null +# compiler_version :integer default(0), not null +# user_selectable :boolean default(FALSE), not null +# hidden :boolean default(FALSE), not null +# color_scheme_id :integer +# remote_theme_id :integer +# +# Indexes +# +# index_themes_on_key (key) +# index_themes_on_remote_theme_id (remote_theme_id) UNIQUE +# diff --git a/app/models/theme_field.rb b/app/models/theme_field.rb new file mode 100644 index 0000000000..65b89db514 --- /dev/null +++ b/app/models/theme_field.rb @@ -0,0 +1,147 @@ +class ThemeField < ActiveRecord::Base + + COMPILER_VERSION = 5 + + belongs_to :theme + + def transpile(es6_source, version) + template = Tilt::ES6ModuleTranspilerTemplate.new {} + wrapped = < { + #{es6_source} +}); +PLUGIN_API_JS + + template.babel_transpile(wrapped) + end + + def process_html(html) + errors = nil + + doc = Nokogiri::HTML.fragment(html) + doc.css('script[type="text/x-handlebars"]').each do |node| + name = node["name"] || node["data-template-name"] || "broken" + is_raw = name =~ /\.raw$/ + if is_raw + template = "require('discourse-common/lib/raw-handlebars').template(#{Barber::Precompiler.compile(node.inner_html)})" + node.replace < + (function() { + Discourse.RAW_TEMPLATES[#{name.sub(/\.raw$/, '').inspect}] = #{template}; + })(); + +COMPILED + else + template = "Ember.HTMLBars.template(#{Barber::Ember::Precompiler.compile(node.inner_html)})" + node.replace < + (function() { + Ember.TEMPLATES[#{name.inspect}] = #{template}; + })(); + +COMPILED + end + + end + + doc.css('script[type="text/discourse-plugin"]').each do |node| + if node['version'].present? + begin + code = transpile(node.inner_html, node['version']) + node.replace("") + rescue MiniRacer::RuntimeError => ex + node.replace("") + errors ||= [] + errors << ex.message + end + end + end + + [doc.to_s, errors&.join("\n")] + end + + + def self.html_fields + %w(body_tag head_tag header footer after_header) + end + + def self.scss_fields + %w(scss embedded_scss) + end + + + def ensure_baked! + if ThemeField.html_fields.include?(self.name) + if !self.value_baked || compiler_version != COMPILER_VERSION + + self.value_baked, self.error = process_html(self.value) + self.compiler_version = COMPILER_VERSION + + if self.value_baked_changed? || compiler_version.changed? || self.error_changed? + self.update_columns(value_baked: value_baked, + compiler_version: compiler_version, + error: error) + end + end + end + end + + def ensure_scss_compiles! + if ThemeField.scss_fields.include?(self.name) + begin + Stylesheet::Compiler.compile("@import \"theme_variables\"; @import \"theme_field\";", + "theme.scss", + theme_field: self.value.dup) + self.error = nil unless error.nil? + rescue SassC::SyntaxError => e + self.error = e.message + end + + if error_changed? + update_columns(error: self.error) + end + + end + end + + def target_name + Theme.targets.invert[target].to_s + end + + before_save do + if value_changed? && !value_baked_changed? + self.value_baked = nil + end + end + + after_commit do + ensure_baked! + ensure_scss_compiles! + + Stylesheet::Manager.clear_theme_cache! if self.name.include?("scss") + + # TODO message for mobile vs desktop + MessageBus.publish "/header-change/#{theme.key}", self.value if theme && self.name == "header" + MessageBus.publish "/footer-change/#{theme.key}", self.value if theme && self.name == "footer" + end +end + +# == Schema Information +# +# Table name: theme_fields +# +# id :integer not null, primary key +# theme_id :integer not null +# target :integer not null +# name :string not null +# value :text not null +# value_baked :text +# created_at :datetime +# updated_at :datetime +# compiler_version :integer default(0), not null +# error :string +# +# Indexes +# +# index_theme_fields_on_theme_id_and_target_and_name (theme_id,target,name) UNIQUE +# diff --git a/app/models/topic.rb b/app/models/topic.rb index 4f1367fa07..2dd0b9b835 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -7,6 +7,7 @@ require_dependency 'text_cleaner' require_dependency 'archetype' require_dependency 'html_prettify' require_dependency 'discourse_tagging' +require_dependency 'search' class Topic < ActiveRecord::Base include ActionView::Helpers::SanitizeHelper @@ -39,12 +40,16 @@ class Topic < ActiveRecord::Base update_category_topic_count_by(-1) if deleted_at.nil? super(trashed_by) update_flagged_posts_count + self.topic_embed.trash! if has_topic_embed? end def recover! update_category_topic_count_by(1) unless deleted_at.nil? super update_flagged_posts_count + unless (topic_embed = TopicEmbed.with_deleted.find_by_topic_id(id)).nil? + topic_embed.recover! + end end rate_limit :default_rate_limiter @@ -117,9 +122,11 @@ class Topic < ActiveRecord::Base has_many :invites, through: :topic_invites, source: :invite has_many :topic_status_updates, dependent: :destroy - has_one :warning + has_one :user_warning has_one :first_post, -> {where post_number: 1}, class_name: Post + has_one :topic_embed, dependent: :destroy + # When we want to temporarily attach some data to a forum topic (usually before serialization) attr_accessor :user_data @@ -883,11 +890,17 @@ SQL end def self.relative_url(id, slug, post_number=nil) - url = "#{Discourse.base_uri}/t/#{slug}/#{id}" + url = "#{Discourse.base_uri}/t/" + url << "#{slug}/" if slug.present? + url << id.to_s url << "/#{post_number}" if post_number.to_i > 1 url end + def slugless_url(post_number=nil) + Topic.relative_url(id, nil, post_number) + end + def relative_url(post_number=nil) Topic.relative_url(id, slug, post_number) end @@ -1174,6 +1187,15 @@ SQL private_topic end + def pm_with_non_human_user? + Topic.private_messages + .joins("LEFT JOIN topic_allowed_groups ON topics.id = topic_allowed_groups.topic_id") + .where("topic_allowed_groups.topic_id IS NULL") + .where("topics.id = ?", self.id) + .where("(SELECT COUNT(*) FROM topic_allowed_users WHERE topic_allowed_users.topic_id = ? AND topic_allowed_users.user_id > 0) = 1", self.id) + .exists? + end + private def update_category_topic_count_by(num) diff --git a/app/models/topic_converter.rb b/app/models/topic_converter.rb index ddbda3a690..dd5bc30559 100644 --- a/app/models/topic_converter.rb +++ b/app/models/topic_converter.rb @@ -7,9 +7,19 @@ class TopicConverter @user = user end - def convert_to_public_topic + def convert_to_public_topic(category_id = nil) Topic.transaction do - @topic.category_id = SiteSetting.allow_uncategorized_topics ? SiteSetting.uncategorized_category_id : Category.where(read_restricted: false).first.id + @topic.category_id = + if category_id + category_id + elsif SiteSetting.allow_uncategorized_topics + SiteSetting.uncategorized_category_id + else + Category.where(read_restricted: false) + .where.not(id: SiteSetting.uncategorized_category_id) + .first.id + end + @topic.archetype = Archetype.default @topic.save update_user_stats diff --git a/app/models/topic_embed.rb b/app/models/topic_embed.rb index 8e45c1d60e..15ca33e40f 100644 --- a/app/models/topic_embed.rb +++ b/app/models/topic_embed.rb @@ -1,11 +1,19 @@ require_dependency 'nokogiri' class TopicEmbed < ActiveRecord::Base + include Trashable + belongs_to :topic belongs_to :post validates_presence_of :embed_url validates_uniqueness_of :embed_url + before_validation(on: :create) do + unless (topic_embed = TopicEmbed.with_deleted.where('deleted_at IS NOT NULL AND embed_url = ?', embed_url).first).nil? + topic_embed.destroy! + end + end + class FetchResponse attr_accessor :title, :body, :author end @@ -203,13 +211,15 @@ end # # Table name: topic_embeds # -# id :integer not null, primary key -# topic_id :integer not null -# post_id :integer not null -# embed_url :string(1000) not null -# content_sha1 :string(40) -# created_at :datetime not null -# updated_at :datetime not null +# id :integer not null, primary key +# topic_id :integer not null +# post_id :integer not null +# embed_url :string(1000) not null +# content_sha1 :string(40) +# created_at :datetime not null +# updated_at :datetime not null +# deleted_at :datetime +# deleted_by_id :integer # # Indexes # diff --git a/app/models/topic_status_update.rb b/app/models/topic_status_update.rb index c8461bd2b7..d5e59c6737 100644 --- a/app/models/topic_status_update.rb +++ b/app/models/topic_status_update.rb @@ -23,7 +23,7 @@ class TopicStatusUpdate < ActiveRecord::Base end after_save do - if (execute_at_changed? || user_id_changed?) && topic + if (execute_at_changed? || user_id_changed?) now = Time.zone.now time = execute_at < now ? now : execute_at @@ -40,7 +40,9 @@ class TopicStatusUpdate < ActiveRecord::Base end def self.ensure_consistency! - TopicStatusUpdate.where("execute_at < ?", Time.zone.now).find_each do |topic_status_update| + TopicStatusUpdate.where("topic_status_updates.execute_at < ?", Time.zone.now) + .find_each do |topic_status_update| + topic_status_update.send( "schedule_auto_#{self.types[topic_status_update.status_type]}_job", topic_status_update.execute_at @@ -76,6 +78,7 @@ class TopicStatusUpdate < ActiveRecord::Base end def schedule_auto_open_job(time) + return unless topic topic.update_status('closed', true, user) if !topic.closed Jobs.enqueue_at(time, :toggle_topic_closed, @@ -85,6 +88,7 @@ class TopicStatusUpdate < ActiveRecord::Base end def schedule_auto_close_job(time) + return unless topic topic.update_status('closed', false, user) if topic.closed Jobs.enqueue_at(time, :toggle_topic_closed, diff --git a/app/models/topic_user.rb b/app/models/topic_user.rb index 49c43e7b4e..7dd30be2df 100644 --- a/app/models/topic_user.rb +++ b/app/models/topic_user.rb @@ -38,7 +38,10 @@ class TopicUser < ActiveRecord::Base end def auto_notification(user_id, topic_id, reason, notification_level) - if TopicUser.where(user_id: user_id, topic_id: topic_id, notifications_reason_id: nil).exists? + if TopicUser.where("user_id = :user_id AND topic_id = :topic_id AND (notifications_reason_id IS NULL OR + (notification_level < :notification_level AND notification_level > :normal_notification_level))", + user_id: user_id, topic_id: topic_id, notification_level: notification_level, + normal_notification_level: notification_levels[:regular]).exists? change(user_id, topic_id, notification_level: notification_level, notifications_reason_id: reason @@ -136,7 +139,17 @@ SQL end if attrs[:notification_level] - MessageBus.publish("/topic/#{topic_id}", { notification_level_change: attrs[:notification_level] }, user_ids: [user_id]) + MessageBus.publish( + "/topic/#{topic_id}", + { notification_level_change: attrs[:notification_level] }, + user_ids: [user_id] + ) + + DiscourseEvent.trigger(:topic_notification_level_changed, + attrs[:notification_level], + user_id, + topic_id + ) end rescue ActiveRecord::RecordNotUnique diff --git a/app/models/upload.rb b/app/models/upload.rb index d9aa27eaf3..1ea66d12d2 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -105,6 +105,9 @@ class Upload < ActiveRecord::Base DistributedMutex.synchronize("upload_#{user_id}_#{filename}") do # do some work on images if FileHelper.is_image?(filename) && is_actual_image?(file) + # retrieve image info + w, h = FastImage.size(file) || [0, 0] + if filename[/\.svg$/i] # whitelist svg elements doc = Nokogiri::XML(file) @@ -112,20 +115,15 @@ class Upload < ActiveRecord::Base File.write(file.path, doc.to_s) file.rewind else - # ensure image isn't huge - w, h = FastImage.size(file) || [0, 0] if w * h >= SiteSetting.max_image_megapixels * 1_000_000 upload.errors.add(:base, I18n.t("upload.images.larger_than_x_megapixels", max_image_megapixels: SiteSetting.max_image_megapixels)) return upload end # fix orientation first - fix_image_orientation(file.path) if should_optimize?(file.path) + fix_image_orientation(file.path) if should_optimize?(file.path, [w, h]) end - # retrieve image info - w, h = FastImage.size(file) || [0, 0] - # default size width, height = ImageSizer.resize(w, h) @@ -156,8 +154,13 @@ class Upload < ActiveRecord::Base end # optimize image (except GIFs, SVGs and large PNGs) - if should_optimize?(file.path) - ImageOptim.new.optimize_image!(file.path) rescue nil + if should_optimize?(file.path, [w, h]) + begin + ImageOptim.new.optimize_image!(file.path) + rescue ImageOptim::Worker::TimeoutExceeded + # Don't optimize if it takes too long + Rails.logger.warn("ImageOptim timed out while optimizing #{filename}") + end # update the file size filesize = File.size(file.path) end @@ -225,11 +228,13 @@ class Upload < ActiveRecord::Base LARGE_PNG_SIZE ||= 3.megabytes - def self.should_optimize?(path) + def self.should_optimize?(path, dimensions = nil) # don't optimize GIFs or SVGs return false if path =~ /\.(gif|svg)$/i return true if path !~ /\.png$/i - w, h = FastImage.size(path) || [0, 0] + + dimensions ||= (FastImage.size(path) || [0, 0]) + w, h = dimensions # don't optimize large PNGs w > 0 && h > 0 && w * h < LARGE_PNG_SIZE end diff --git a/app/models/user.rb b/app/models/user.rb index 8c4a0a130a..f3d7e325c6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -37,7 +37,7 @@ class User < ActiveRecord::Base has_many :invites, dependent: :destroy has_many :topic_links, dependent: :destroy has_many :uploads - has_many :warnings + has_many :user_warnings has_many :user_archived_messages, dependent: :destroy has_many :email_change_requests, dependent: :destroy has_many :directory_items, dependent: :delete_all @@ -162,9 +162,15 @@ class User < ActiveRecord::Base def self.username_available?(username) lower = username.downcase + !reserved_username?(lower) && !User.where(username_lower: lower).exists? + end - User.where(username_lower: lower).blank? && - SiteSetting.reserved_usernames.split("|").all? { |reserved| !lower.match('^' + Regexp.escape(reserved).gsub('\*', '.*') + '$') } + def self.reserved_username?(username) + lower = username.downcase + + SiteSetting.reserved_usernames.split("|").any? do |reserved| + !!lower.match("^#{Regexp.escape(reserved).gsub('\*', '.*')}$") + end end def self.plugin_staff_user_custom_fields @@ -270,7 +276,7 @@ class User < ActiveRecord::Base def approve(approved_by, send_mail=true) self.approved = true - if approved_by.is_a?(Fixnum) + if approved_by.is_a?(Integer) self.approved_by_id = approved_by else self.approved_by = approved_by @@ -608,7 +614,7 @@ class User < ActiveRecord::Base end def warnings_received_count - warnings.count + user_warnings.count end def flags_received_count @@ -734,7 +740,7 @@ class User < ActiveRecord::Base (tl_badge + other_badges).take(limit) end - def self.count_by_signup_date(start_date, end_date, group_id=nil) + def self.count_by_signup_date(start_date, end_date, group_id = nil) result = where('users.created_at >= ? AND users.created_at <= ?', start_date, end_date) if group_id @@ -791,7 +797,7 @@ class User < ActiveRecord::Base end def find_email - last_sent_email_address || email + last_sent_email_address.present? && EmailValidator.email_regex =~ last_sent_email_address ? last_sent_email_address : email end def tl3_requirements @@ -883,10 +889,6 @@ class User < ActiveRecord::Base .count end - def number_of_warnings - self.warnings.count - end - def number_of_suspensions UserHistory.for(self, :suspend_user).count end diff --git a/app/models/user_history.rb b/app/models/user_history.rb index a6677de389..e177cd8de4 100644 --- a/app/models/user_history.rb +++ b/app/models/user_history.rb @@ -19,8 +19,8 @@ class UserHistory < ActiveRecord::Base @actions ||= Enum.new(delete_user: 1, change_trust_level: 2, change_site_setting: 3, - change_site_customization: 4, - delete_site_customization: 5, + change_theme: 4, + delete_theme: 5, checked_for_custom_avatar: 6, # not used anymore notified_about_avatar: 7, notified_about_sequential_replies: 8, @@ -71,8 +71,8 @@ class UserHistory < ActiveRecord::Base @staff_actions ||= [:delete_user, :change_trust_level, :change_site_setting, - :change_site_customization, - :delete_site_customization, + :change_theme, + :delete_theme, :change_site_text, :suspend_user, :unsuspend_user, @@ -158,7 +158,7 @@ class UserHistory < ActiveRecord::Base end def new_value_is_json? - [UserHistory.actions[:change_site_customization], UserHistory.actions[:delete_site_customization]].include?(action) + [UserHistory.actions[:change_theme], UserHistory.actions[:delete_theme]].include?(action) end def previous_value_is_json? diff --git a/app/models/warning.rb b/app/models/user_warning.rb similarity index 69% rename from app/models/warning.rb rename to app/models/user_warning.rb index 0d177fb0c6..dd89c7f995 100644 --- a/app/models/warning.rb +++ b/app/models/user_warning.rb @@ -1,4 +1,4 @@ -class Warning < ActiveRecord::Base +class UserWarning < ActiveRecord::Base belongs_to :user belongs_to :topic belongs_to :created_by, class_name: 'User' @@ -6,7 +6,7 @@ end # == Schema Information # -# Table name: warnings +# Table name: user_warnings # # id :integer not null, primary key # topic_id :integer not null @@ -17,6 +17,6 @@ end # # Indexes # -# index_warnings_on_topic_id (topic_id) UNIQUE -# index_warnings_on_user_id (user_id) +# index_user_warnings_on_topic_id (topic_id) UNIQUE +# index_user_warnings_on_user_id (user_id) # diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 4159dfbc7c..40fed09de6 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -47,36 +47,6 @@ class WebHook < ActiveRecord::Base def self.enqueue_post_hooks(event, post, user=nil) WebHook.enqueue_hooks(:post, post_id: post.id, category_id: post&.topic&.category_id, event_name: event.to_s) end - - %i(topic_destroyed topic_recovered).each do |event| - DiscourseEvent.on(event) do |topic, user| - WebHook.enqueue_topic_hooks(event, topic, user) - end - end - - DiscourseEvent.on(:topic_created) do |topic, _, user| - WebHook.enqueue_topic_hooks(:topic_created, topic, user) - end - - %i(post_created - post_destroyed - post_recovered).each do |event| - - DiscourseEvent.on(event) do |post, _, user| - WebHook.enqueue_post_hooks(event, post, user) - end - end - - DiscourseEvent.on(:post_edited) do |post, topic_changed| - WebHook.enqueue_post_hooks(:post_edited, post) - WebHook.enqueue_topic_hooks(:topic_edited, post.topic) if post.is_first_post? && topic_changed - end - - %i(user_created user_approved user_updated).each do |event| - DiscourseEvent.on(event) do |user| - WebHook.enqueue_hooks(:user, user_id: user.id, event_name: event.to_s) - end - end end # == Schema Information diff --git a/app/serializers/basic_group_serializer.rb b/app/serializers/basic_group_serializer.rb index b37184e723..ea3df8e08a 100644 --- a/app/serializers/basic_group_serializer.rb +++ b/app/serializers/basic_group_serializer.rb @@ -19,7 +19,8 @@ class BasicGroupSerializer < ApplicationSerializer :bio_cooked, :public, :allow_membership_requests, - :full_name + :full_name, + :default_notification_level def include_incoming_email? staff? diff --git a/app/serializers/color_scheme_color_serializer.rb b/app/serializers/color_scheme_color_serializer.rb index b1d3d809b6..3e99c06cf2 100644 --- a/app/serializers/color_scheme_color_serializer.rb +++ b/app/serializers/color_scheme_color_serializer.rb @@ -6,6 +6,11 @@ class ColorSchemeColorSerializer < ApplicationSerializer end def default_hex - ColorScheme.base_colors[object.name] + if object.color_scheme + object.color_scheme.base_colors[object.name] + else + # it is a base color so it is already default + object.hex + end end end diff --git a/app/serializers/color_scheme_serializer.rb b/app/serializers/color_scheme_serializer.rb index 965d592377..f119d79a75 100644 --- a/app/serializers/color_scheme_serializer.rb +++ b/app/serializers/color_scheme_serializer.rb @@ -1,8 +1,8 @@ class ColorSchemeSerializer < ApplicationSerializer - attributes :id, :name, :enabled, :is_base + attributes :id, :name, :is_base, :base_scheme_id, :theme_id, :theme_name has_many :colors, serializer: ColorSchemeColorSerializer, embed: :objects - def base - object.is_base || false + def theme_name + object.theme&.name end end diff --git a/app/serializers/flagged_user_serializer.rb b/app/serializers/flagged_user_serializer.rb index 899602b9fe..c9ec0aa6e8 100644 --- a/app/serializers/flagged_user_serializer.rb +++ b/app/serializers/flagged_user_serializer.rb @@ -3,7 +3,6 @@ class FlaggedUserSerializer < BasicUserSerializer :can_be_deleted, :post_count, :topic_count, - :email, :ip_address def can_delete_all_posts diff --git a/app/serializers/site_customization_serializer.rb b/app/serializers/site_customization_serializer.rb deleted file mode 100644 index 6a3e70ff21..0000000000 --- a/app/serializers/site_customization_serializer.rb +++ /dev/null @@ -1,7 +0,0 @@ -class SiteCustomizationSerializer < ApplicationSerializer - - attributes :id, :name, :key, :enabled, :created_at, :updated_at, - :stylesheet, :header, :footer, :top, - :mobile_stylesheet, :mobile_header, :mobile_footer, :mobile_top, - :head_tag, :body_tag, :embedded_css -end diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb index d43c165a96..5ea12e8cbe 100644 --- a/app/serializers/site_serializer.rb +++ b/app/serializers/site_serializer.rb @@ -24,13 +24,25 @@ class SiteSerializer < ApplicationSerializer :tags_filter_regexp, :top_tags, :wizard_required, - :topic_featured_link_allowed_category_ids + :topic_featured_link_allowed_category_ids, + :user_themes has_many :categories, serializer: BasicCategorySerializer, embed: :objects has_many :trust_levels, embed: :objects has_many :archetypes, embed: :objects, serializer: ArchetypeSerializer has_many :user_fields, embed: :objects, serialzer: UserFieldSerializer + def user_themes + cache_fragment("user_themes") do + Theme.where('key = :default OR user_selectable', + default: SiteSetting.default_theme_key) + .order(:name) + .pluck(:key, :name) + .map{|k,n| {theme_key: k, name: n, default: k == SiteSetting.default_theme_key}} + .as_json + end + end + def groups cache_fragment("group_names") do Group.order(:name).pluck(:id,:name).map { |id,name| { id: id, name: name } }.as_json diff --git a/app/serializers/theme_serializer.rb b/app/serializers/theme_serializer.rb new file mode 100644 index 0000000000..ab9af520a0 --- /dev/null +++ b/app/serializers/theme_serializer.rb @@ -0,0 +1,50 @@ +class ThemeFieldSerializer < ApplicationSerializer + attributes :name, :target, :value, :error + + def target + case object.target + when 0 then "common" + when 1 then "desktop" + when 2 then "mobile" + end + end + + def include_error? + object.error.present? + end +end + +class ChildThemeSerializer < ApplicationSerializer + attributes :id, :name, :key, :created_at, :updated_at, :default + + def include_default? + object.key == SiteSetting.default_theme_key + end + + def default + true + end +end + +class RemoteThemeSerializer < ApplicationSerializer + attributes :id, :remote_url, :remote_version, :local_version, :about_url, + :license_url, :commits_behind, :remote_updated_at, :updated_at + + # wow, AMS has some pretty nutty logic where it tries to find the path here + # from action dispatch, tell it not to + def about_url + object.about_url + end +end + +class ThemeSerializer < ChildThemeSerializer + attributes :color_scheme, :color_scheme_id, :user_selectable, :remote_theme_id + + has_many :theme_fields, serializer: ThemeFieldSerializer, embed: :objects + has_many :child_themes, serializer: ChildThemeSerializer, embed: :objects + has_one :remote_theme, serializer: RemoteThemeSerializer, embed: :objects + + def child_themes + object.child_themes.order(:name) + end +end diff --git a/app/serializers/topic_view_serializer.rb b/app/serializers/topic_view_serializer.rb index 848c3e3f02..c438bacb6f 100644 --- a/app/serializers/topic_view_serializer.rb +++ b/app/serializers/topic_view_serializer.rb @@ -3,6 +3,7 @@ require_dependency 'new_post_manager' class TopicViewSerializer < ApplicationSerializer include PostStreamSerializerMixin + include ApplicationHelper def self.attributes_from_topic(*list) [list].flatten.each do |attribute| @@ -33,7 +34,8 @@ class TopicViewSerializer < ApplicationSerializer :word_count, :deleted_at, :pending_posts_count, - :user_id + :user_id, + :pm_with_non_human_user? attributes :draft, :draft_key, @@ -58,7 +60,8 @@ class TopicViewSerializer < ApplicationSerializer :message_archived, :tags, :featured_link, - :topic_status_update + :topic_status_update, + :unicode_title # TODO: Split off into proper object / serializer def details @@ -69,7 +72,7 @@ class TopicViewSerializer < ApplicationSerializer last_poster: BasicUserSerializer.new(topic.last_poster, scope: scope, root: false) } - if object.topic.private_message? + if private_message?(topic) allowed_user_ids = Set.new result[:allowed_groups] = object.topic.allowed_groups.map do |group| @@ -127,7 +130,7 @@ class TopicViewSerializer < ApplicationSerializer end def is_warning - object.topic.private_message? && object.topic.subtype == TopicSubtype.moderator_warning + private_message?(object.topic) && object.topic.subtype == TopicSubtype.moderator_warning end def include_is_warning? @@ -147,7 +150,7 @@ class TopicViewSerializer < ApplicationSerializer end def include_message_archived? - object.topic.private_message? + private_message?(object.topic) end def message_archived @@ -264,4 +267,22 @@ class TopicViewSerializer < ApplicationSerializer object.topic.featured_link end + def include_unicode_title? + !!(object.topic.title =~ /:([\w\-+]*):/) + end + + def unicode_title + gsub_emoji_to_unicode(object.topic.title) + end + + def include_pm_with_non_human_user? + private_message?(object.topic) + end + + private + + def private_message?(topic) + @private_message ||= topic.private_message? + end + end diff --git a/app/serializers/user_history_serializer.rb b/app/serializers/user_history_serializer.rb index 039e25c61d..f73d3e424b 100644 --- a/app/serializers/user_history_serializer.rb +++ b/app/serializers/user_history_serializer.rb @@ -12,7 +12,8 @@ class UserHistorySerializer < ApplicationSerializer :post_id, :category_id, :action, - :custom_type + :custom_type, + :id has_one :acting_user, serializer: BasicUserSerializer, embed: :objects has_one :target_user, serializer: BasicUserSerializer, embed: :objects diff --git a/app/serializers/web_hook_post_serializer.rb b/app/serializers/web_hook_post_serializer.rb index 221c398fa7..5d79ef5f48 100644 --- a/app/serializers/web_hook_post_serializer.rb +++ b/app/serializers/web_hook_post_serializer.rb @@ -1,17 +1,17 @@ class WebHookPostSerializer < PostSerializer - def include_can_edit? - false + def include_topic_title? + true end - def can_delete - false - end - - def can_recover - false - end - - def can_wiki - false + %i{ + can_view + can_edit + can_delete + can_recover + can_wiki + }.each do |attr| + define_method("include_#{attr}?") do + false + end end end diff --git a/app/services/color_scheme_revisor.rb b/app/services/color_scheme_revisor.rb index 5db7bb79d1..ce66c37332 100644 --- a/app/services/color_scheme_revisor.rb +++ b/app/services/color_scheme_revisor.rb @@ -9,63 +9,26 @@ class ColorSchemeRevisor self.new(color_scheme, params).revise end - def self.revert(color_scheme) - self.new(color_scheme).revert - end - def revise ColorScheme.transaction do - if @params[:enabled] - ColorScheme.where('id != ?', @color_scheme.id).update_all enabled: false - end @color_scheme.name = @params[:name] if @params.has_key?(:name) - @color_scheme.enabled = @params[:enabled] if @params.has_key?(:enabled) - @color_scheme.theme_id = @params[:theme_id] if @params.has_key?(:theme_id) - new_version = false + @color_scheme.base_scheme_id = @params[:base_scheme_id] if @params.has_key?(:base_scheme_id) + has_colors = @params[:colors] - if @params[:colors] - new_version = @params[:colors].any? do |c| - (existing = @color_scheme.colors_by_name[c[:name]]).nil? or existing.hex != c[:hex] - end - end - - if new_version - ColorScheme.create( - name: @color_scheme.name, - enabled: false, - colors: @color_scheme.colors_hashes, - versioned_id: @color_scheme.id, - version: @color_scheme.version) - @color_scheme.version += 1 - end - - if @params[:colors] + if has_colors @params[:colors].each do |c| if existing = @color_scheme.colors_by_name[c[:name]] existing.update_attributes(c) + else + @color_scheme.color_scheme_colors << ColorSchemeColor.new(name: c[:name], hex: c[:hex]) end end - end - - @color_scheme.save - @color_scheme.clear_colors_cache - end - @color_scheme - end - - def revert - ColorScheme.transaction do - if prev = @color_scheme.previous_version - @color_scheme.version = prev.version - @color_scheme.colors.clear - prev.colors.update_all(color_scheme_id: @color_scheme.id) - prev.destroy - @color_scheme.save! @color_scheme.clear_colors_cache end - end + @color_scheme.save if has_colors || @color_scheme.name_changed? || @color_scheme.base_scheme_id_changed? + end @color_scheme end diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb index 71734ab2e2..1cd09176af 100644 --- a/app/services/staff_action_logger.rb +++ b/app/services/staff_action_logger.rb @@ -113,34 +113,49 @@ class StaffActionLogger })) end - SITE_CUSTOMIZATION_LOGGED_ATTRS = [ - 'stylesheet', 'mobile_stylesheet', - 'header', 'mobile_header', - 'top', 'mobile_top', - 'footer', 'mobile_footer', - 'head_tag', - 'body_tag', - 'position', - 'enabled', - 'key' - ] + def theme_json(theme) + ThemeSerializer.new(theme, root:false).to_json + end + + def strip_duplicates(old,cur) + return [old,cur] unless old && cur + + old = JSON.parse(old) + cur = JSON.parse(cur) + + old.each do |k, v| + next if k == "name" + next if k == "id" + if (v == cur[k]) + cur.delete(k) + old.delete(k) + end + end + + [old.to_json, cur.to_json] + end + + def log_theme_change(old_json, new_theme, opts={}) + raise Discourse::InvalidParameters.new(:new_theme) unless new_theme + + new_json = theme_json(new_theme) + + old_json,new_json = strip_duplicates(old_json,new_json) - def log_site_customization_change(old_record, site_customization_params, opts={}) - raise Discourse::InvalidParameters.new(:site_customization_params) unless site_customization_params UserHistory.create( params(opts).merge({ - action: UserHistory.actions[:change_site_customization], - subject: site_customization_params[:name], - previous_value: old_record ? old_record.attributes.slice(*SITE_CUSTOMIZATION_LOGGED_ATTRS).to_json : nil, - new_value: site_customization_params.slice(*(SITE_CUSTOMIZATION_LOGGED_ATTRS.map(&:to_sym))).to_json + action: UserHistory.actions[:change_theme], + subject: new_theme.name, + previous_value: old_json, + new_value: new_json })) end - def log_site_customization_destroy(site_customization, opts={}) - raise Discourse::InvalidParameters.new(:site_customization) unless site_customization + def log_theme_destroy(theme, opts={}) + raise Discourse::InvalidParameters.new(:theme) unless theme UserHistory.create( params(opts).merge({ - action: UserHistory.actions[:delete_site_customization], - subject: site_customization.name, - previous_value: site_customization.attributes.slice(*SITE_CUSTOMIZATION_LOGGED_ATTRS).to_json + action: UserHistory.actions[:delete_theme], + subject: theme.name, + previous_value: theme_json(theme) })) end diff --git a/app/views/common/_discourse_javascript.html.erb b/app/views/common/_discourse_javascript.html.erb index 9e2da0a900..05e0a95f94 100644 --- a/app/views/common/_discourse_javascript.html.erb +++ b/app/views/common/_discourse_javascript.html.erb @@ -65,4 +65,4 @@ })(); -<%= script 'browser-update' %> +<%= preload_script 'browser-update' %> diff --git a/app/views/common/_discourse_stylesheet.html.erb b/app/views/common/_discourse_stylesheet.html.erb index 702bbd9f02..044ce2fc76 100644 --- a/app/views/common/_discourse_stylesheet.html.erb +++ b/app/views/common/_discourse_stylesheet.html.erb @@ -1,13 +1,13 @@ <%- if rtl? %> - <%= DiscourseStylesheets.stylesheet_link_tag(mobile_view? ? :mobile_rtl : :desktop_rtl) %> + <%= discourse_stylesheet_link_tag(mobile_view? ? :mobile_rtl : :desktop_rtl) %> <%- else %> - <%= DiscourseStylesheets.stylesheet_link_tag(mobile_view? ? :mobile : :desktop) %> + <%= discourse_stylesheet_link_tag(mobile_view? ? :mobile : :desktop) %> <%- end %> <%- if staff? %> - <%= DiscourseStylesheets.stylesheet_link_tag(:admin) %> + <%= discourse_stylesheet_link_tag(:admin) %> <%- end %> -<%- unless customization_disabled? %> - <%= SiteCustomization.custom_stylesheet(session[:preview_style], mobile_view? ? :mobile : :desktop) %> +<%- if theme_key %> + <%= discourse_stylesheet_link_tag(mobile_view? ? :mobile_theme : :desktop_theme) %> <%- end %> diff --git a/app/views/common/_special_font_face.html.erb b/app/views/common/_special_font_face.html.erb index 8e95e5ece8..6436768afd 100644 --- a/app/views/common/_special_font_face.html.erb +++ b/app/views/common/_special_font_face.html.erb @@ -9,11 +9,13 @@ %> <% font_domain = "#{request.protocol}#{request.host_with_port}&2" %> +<% woff2_url = "#{asset_path("fontawesome-webfont.woff2")}?#{font_domain}&v=4.7.0".html_safe %> + diff --git a/app/views/email/_mailing_list_post.html.erb b/app/views/email/_mailing_list_post.html.erb new file mode 100644 index 0000000000..43d09820e2 --- /dev/null +++ b/app/views/email/_mailing_list_post.html.erb @@ -0,0 +1,5 @@ +
  • + <%= raw format_topic_title(topic.title) %> + <%= post_count %> + <%= category_badge(topic.category, inline_style: true, absolute_url: true) %> +
  • diff --git a/app/views/email/_secure_mailing_list_post.html.erb b/app/views/email/_secure_mailing_list_post.html.erb new file mode 100644 index 0000000000..418a6b79fa --- /dev/null +++ b/app/views/email/_secure_mailing_list_post.html.erb @@ -0,0 +1,4 @@ +
  • + <%= email_topic_link(topic) %> + <%= post_count %> +
  • diff --git a/app/views/email/notification.html.erb b/app/views/email/notification.html.erb index 5e33b4625e..5809973d56 100644 --- a/app/views/email/notification.html.erb +++ b/app/views/email/notification.html.erb @@ -2,28 +2,32 @@
    %{header_instructions}
    - <%= render partial: 'email/post', locals: { post: post, use_excerpt: false } %> + <%- if SiteSetting.private_email? %> +

    <%= t('system_messages.contents_hidden') %>

    + <% else %> + <%= render partial: 'email/post', locals: { post: post, use_excerpt: false } %> - <% if in_reply_to_post.present? || context_posts.present? %> - -
    - <% end %> - - <% if in_reply_to_post.present? %> -

    <%= t "user_notifications.in_reply_to" %>

    - <%= render partial: 'email/post', locals: { post: in_reply_to_post, use_excerpt: true} %> - <% end %> - - <% if context_posts.present? %> -

    <%= t "user_notifications.previous_discussion" %>

    - <% context_posts.each do |p| %> - <%= render partial: 'email/post', locals: { post: p, use_excerpt: false } %> + <% if in_reply_to_post.present? || context_posts.present? %> + +
    <% end %> - <% end %> - <% if reached_limit %> -
    - + <% if in_reply_to_post.present? %> +

    <%= t "user_notifications.in_reply_to" %>

    + <%= render partial: 'email/post', locals: { post: in_reply_to_post, use_excerpt: true} %> + <% end %> + + <% if context_posts.present? %> +

    <%= t "user_notifications.previous_discussion" %>

    + <% context_posts.each do |p| %> + <%= render partial: 'email/post', locals: { post: p, use_excerpt: false } %> + <% end %> + <% end %> + + <% if reached_limit %> +
    + + <% end %> <% end %> @@ -33,7 +37,7 @@
    - +
    diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 41b321f22d..984b0e198a 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -4,9 +4,8 @@ <%= content_for?(:title) ? yield(:title) + ' - ' + SiteSetting.title : SiteSetting.title %> + <%= render partial: "layouts/head" %> - <%= render partial: "common/special_font_face" %> - <%= render partial: "common/discourse_stylesheet" %> <%= discourse_csrf_tags %> <%- if SiteSetting.enable_escaped_fragments? %> @@ -22,26 +21,26 @@ window.EmberENV['FORCE_JQUERY'] = true; - <%= script "locales/#{I18n.locale}" %> - <%= script "ember_jquery" %> - <%= script "preload-store" %> - <%= script "vendor" %> - <%= script "pretty-text-bundle" %> - <%= script "application" %> + <%= preload_script "locales/#{I18n.locale}" %> + <%= preload_script "ember_jquery" %> + <%= preload_script "preload-store" %> + <%= preload_script "vendor" %> + <%= preload_script "pretty-text-bundle" %> + <%= preload_script "application" %> <%- if allow_plugins? %> - <%= script "plugin" %> + <%= preload_script "plugin" %> <%- end %> <%- if allow_third_party_plugins? %> - <%= script "plugin-third-party" %> + <%= preload_script "plugin-third-party" %> <%- end %> <%- if staff? %> - <%= script "admin" %> + <%= preload_script "admin" %> <%- end %> <%- unless customization_disabled? %> - <%= raw SiteCustomization.custom_head_tag(session[:preview_style]) %> + <%= raw theme_lookup("head_tag") %> <%- end %> <%= render_google_universal_analytics_code %> @@ -51,7 +50,12 @@ <%- end %> + <%= render partial: "common/discourse_stylesheet" %> + <%= render partial: "common/special_font_face" %> + <%= yield :head %> + + <%= raw build_plugin_html 'before-head-close' %> @@ -82,7 +86,7 @@ <%- unless customization_disabled? || loading_admin? %> - <%= SiteCustomization.custom_header(session[:preview_style], mobile_view? ? :mobile : :desktop) %> + <%= theme_lookup("header") %> <%- end %>
    @@ -118,7 +122,8 @@ <%= render_google_analytics_code %> <%- unless customization_disabled? %> - <%= raw SiteCustomization.custom_body_tag(session[:preview_style]) %> + <%= raw theme_lookup("body_tag") %> <%- end %> + <%= raw build_plugin_html 'before-body-close' %> diff --git a/app/views/layouts/crawler.html.erb b/app/views/layouts/crawler.html.erb index e4e7f9a47e..ac757cc8c2 100644 --- a/app/views/layouts/crawler.html.erb +++ b/app/views/layouts/crawler.html.erb @@ -6,20 +6,16 @@ <%= render partial: "layouts/head" %> <%- if rtl? %> - <%= DiscourseStylesheets.stylesheet_link_tag(mobile_view? ? :mobile_rtl : :desktop_rtl) %> + <%= discourse_stylesheet_link_tag(mobile_view? ? :mobile_rtl : :desktop_rtl) %> <%- else %> - <%= DiscourseStylesheets.stylesheet_link_tag(mobile_view? ? :mobile : :desktop) %> - <%- end %> - <%- unless customization_disabled? %> - <%= raw SiteCustomization.custom_head_tag(session[:preview_style]) %> + <%= discourse_stylesheet_link_tag(mobile_view? ? :mobile : :desktop) %> <%- end %> + <%= theme_lookup("head_tag") %> <%= render_google_universal_analytics_code %> <%= yield :head %> - <%- unless customization_disabled? %> - <%= SiteCustomization.custom_header(session[:preview_style], mobile_view? ? :mobile : :desktop) %> - <%- end %> + <%= theme_lookup("header") %>
    ">
    @@ -37,9 +33,7 @@

    <%= t 'powered_by_html' %>

    <%= render_google_analytics_code %> - <%- unless customization_disabled? %> - <%= raw SiteCustomization.custom_body_tag(session[:preview_style]) %> - <%- end %> + <%= theme_lookup("body_tag") %> <%= yield :after_body %> diff --git a/app/views/layouts/embed.html.erb b/app/views/layouts/embed.html.erb index b4ee7cfa7c..f1a66ee52b 100644 --- a/app/views/layouts/embed.html.erb +++ b/app/views/layouts/embed.html.erb @@ -3,11 +3,11 @@ - <%= stylesheet_link_tag 'embed' %> + <%= discourse_stylesheet_link_tag 'embed', theme_key: nil %> <%- unless customization_disabled? %> - <%= SiteCustomization.custom_stylesheet(session[:preview_style], :embedded) %> + <%= discourse_stylesheet_link_tag :embedded_theme %> <%- end %> - <%= javascript_include_tag 'break_string' %> + <%= preload_script 'break_string' %> <%- if @topic_view && @topic_view.page_title.present? %> <%= @topic_view.page_title %> - <%= SiteSetting.title %> diff --git a/app/views/layouts/finish_installation.html.erb b/app/views/layouts/finish_installation.html.erb index 31ebd26fa7..abf274f54f 100644 --- a/app/views/layouts/finish_installation.html.erb +++ b/app/views/layouts/finish_installation.html.erb @@ -1,9 +1,9 @@ - <%= stylesheet_link_tag 'wizard' %> + <%= discourse_stylesheet_link_tag 'wizard', theme_key: nil %> <%= render partial: "common/special_font_face" %> - <%= script 'jquery' %> - <%= script 'wizard-vendor' %> + <%= preload_script 'ember_jquery' %> + <%= preload_script 'wizard-vendor' %> <%= render partial: "layouts/head" %> <%= t 'wizard.title' %> diff --git a/app/views/layouts/no_ember.html.erb b/app/views/layouts/no_ember.html.erb index 2c6b3f74be..035a84027b 100644 --- a/app/views/layouts/no_ember.html.erb +++ b/app/views/layouts/no_ember.html.erb @@ -9,23 +9,17 @@ <%= render partial: "common/discourse_stylesheet" %> <%= discourse_csrf_tags %> - <%- unless customization_disabled? %> - <%= raw SiteCustomization.custom_head_tag(session[:preview_style]) %> - <%- end %> + <%= theme_lookup("head_tag") %> <%= yield(:no_ember_head) %> class="<%= @custom_body_class %>"<% end %>> - <%- unless customization_disabled? %> - <%= SiteCustomization.custom_header(session[:preview_style], mobile_view? ? :mobile : :desktop) %> - <%- end %> + <%= theme_lookup("header") %>
    <%= render partial: 'header' %>
    <%= yield %>
    - <%- unless customization_disabled? %> - <%= SiteCustomization.custom_footer(session[:preview_style], mobile_view? ? :mobile : :desktop) %> - <%- end %> + <%= theme_lookup("footer") %> diff --git a/app/views/search/show.html.erb b/app/views/search/show.html.erb index e69de29bb2..4bc06c6f46 100644 --- a/app/views/search/show.html.erb +++ b/app/views/search/show.html.erb @@ -0,0 +1 @@ +<% content_for :title do %><%= t 'js.search.results_page' %><% end %> diff --git a/app/views/topics/show.html.erb b/app/views/topics/show.html.erb index e24626f203..606485c7ca 100644 --- a/app/views/topics/show.html.erb +++ b/app/views/topics/show.html.erb @@ -100,7 +100,7 @@ <% end %> <% end %> -<% content_for(:title) { "#{@topic_view.page_title}" } %> +<% content_for(:title) { "#{gsub_emoji_to_unicode(@topic_view.page_title)}" } %> <% if @topic_view.print %> <% content_for :after_body do %> diff --git a/app/views/user_notifications/mailing_list.html.erb b/app/views/user_notifications/mailing_list.html.erb index 006098165a..7b91858c62 100644 --- a/app/views/user_notifications/mailing_list.html.erb +++ b/app/views/user_notifications/mailing_list.html.erb @@ -21,21 +21,13 @@

    <%= t('user_notifications.mailing_list.new_topics') %>

      <% @new_topic_posts.keys.each do |topic| %> -
    • - <%= raw format_topic_title(topic.title) %> - <%= @new_topic_posts[topic].length %> - <%= category_badge(topic.category, inline_style: true, absolute_url: true) %> -
    • + <%= mailing_list_topic(topic, @new_topic_posts[topic].length) %> <% end %>

    <%= t('user_notifications.mailing_list.topic_updates') %>

      <% @existing_topic_posts.keys.each do |topic| %> -
    • - <%= raw format_topic_title(topic.title) %> - <%= @existing_topic_posts[topic].length %> - <%= category_badge(topic.category, inline_style: true, absolute_url: true) %> -
    • + <%= mailing_list_topic(topic, @existing_topic_posts[topic].length) %> <% end %>
    @@ -46,40 +38,42 @@

    - <%= raw format_topic_title(topic.title) %> + <%= email_topic_link(topic) %>

    - - - <% @posts_by_topic[topic].each do |post| %> -
    - -

    - - <%- if show_username_on_post(post) %> - <%= post.user.username %> - <% end %> + <%- unless SiteSetting.private_email? %> + + + <% @posts_by_topic[topic].each do |post| %> +

    + +

    + + <%- if show_username_on_post(post) %> + <%= post.user.username %> + <% end %> - <%- if show_name_on_post(post) %> - - <%= post.user.name %> - <% end %> - + <%- if show_name_on_post(post) %> + - <%= post.user.name %> + <% end %> + - - - <%= I18n.l(post.created_at, format: :long) %> -

    - <%= raw format_for_email(post, false) %> -
    -
    - <% end %> - <%= t('user_notifications.mailing_list.view_this_topic') %> - - - - - <%= t('user_notifications.mailing_list.back_to_top') %> - - + - + <%= I18n.l(post.created_at, format: :long) %> +

    + <%= raw format_for_email(post, false) %> +
    +
    + <% end %> + <%= t('user_notifications.mailing_list.view_this_topic') %> + + + + + <%= t('user_notifications.mailing_list.back_to_top') %> + + + <% end %> <% end %> diff --git a/app/views/user_notifications/mailing_list.text.erb b/app/views/user_notifications/mailing_list.text.erb index 5208a9f085..dfe0fe1683 100644 --- a/app/views/user_notifications/mailing_list.text.erb +++ b/app/views/user_notifications/mailing_list.text.erb @@ -4,28 +4,30 @@ <%- if @new_topic_posts.keys.present? -%> ### <%= t 'user_notifications.mailing_list.new_topics' %> <%- @new_topic_posts.keys.each do |topic| %> - <%= raw(@markdown_linker.create(topic.title, topic.relative_url)) %> + <%= mailing_list_topic_text(topic) %> <%- end -%> <%- end -%> <%- if @existing_topic_posts.keys.present? -%> ### <%= t 'user_notifications.mailing_list.new_topics' %> <%- @existing_topic_posts.keys.each do |topic| -%> - <%= raw(@markdown_linker.create(topic.title, topic.relative_url)) %> + <%= mailing_list_topic_text(topic) %> <%= @existing_topic_posts[topic].length %> <%- end -%> <%- end -%> -------------------------------------------------------------------------------- -<%- @posts_by_topic.keys.each do |topic| %> - ### <%= raw(@markdown_linker.create(topic.title, topic.relative_url)) %> +<%- unless SiteSetting.private_email? %> + <%- @posts_by_topic.keys.each do |topic| %> + ### <%= raw(@markdown_linker.create(topic.title, topic.relative_url)) %> - <%- @posts_by_topic[topic].each do |post| -%> - <%= post.user.name || post.user.username %> - <%= post.created_at %> --------------------------------------------------------------------------------- - <%= post.raw %> + <%- @posts_by_topic[topic].each do |post| -%> + <%= post.user.name || post.user.username %> - <%= post.created_at %> + -------------------------------------------------------------------------------- + <%= post.raw %> - - <%- end -%> + + <%- end -%> + <%- end %> <%- end %> diff --git a/app/views/users/activate_account.html.erb b/app/views/users/activate_account.html.erb index 97cf7faecf..44aac1f420 100644 --- a/app/views/users/activate_account.html.erb +++ b/app/views/users/activate_account.html.erb @@ -10,8 +10,8 @@
    <%- content_for(:no_ember_head) do %> - <%= script "ember_jquery" %> - <%= script "vendor" %> + <%= preload_script "ember_jquery" %> + <%= preload_script "vendor" %> <%= render_google_universal_analytics_code %> <%- end %> diff --git a/app/views/users/password_reset.html.erb b/app/views/users/password_reset.html.erb index e64d9d8197..a7d6aedca4 100644 --- a/app/views/users/password_reset.html.erb +++ b/app/views/users/password_reset.html.erb @@ -17,7 +17,7 @@ <%- content_for(:no_ember_head) do %> - <%= script "ember_jquery" %> + <%= preload_script "ember_jquery" %> <%= render_google_universal_analytics_code %> <%- end %> diff --git a/app/views/wizard/index.html.erb b/app/views/wizard/index.html.erb index a0f193526f..b090b4539c 100644 --- a/app/views/wizard/index.html.erb +++ b/app/views/wizard/index.html.erb @@ -1,10 +1,10 @@ - <%= stylesheet_link_tag 'wizard' %> - <%= script 'ember_jquery' %> - <%= script 'wizard-vendor' %> - <%= script 'wizard-application' %> - <%= script "locales/#{I18n.locale}" %> + <%= discourse_stylesheet_link_tag :wizard, theme_key: nil %> + <%= preload_script 'ember_jquery' %> + <%= preload_script 'wizard-vendor' %> + <%= preload_script 'wizard-application' %> + <%= preload_script "locales/#{I18n.locale}" %> <%= render partial: "common/special_font_face" %> <%= csrf_meta_tags %> diff --git a/app/views/wizard/qunit.html.erb b/app/views/wizard/qunit.html.erb index 2c4fa085e7..4e6e7a3a59 100644 --- a/app/views/wizard/qunit.html.erb +++ b/app/views/wizard/qunit.html.erb @@ -4,7 +4,7 @@ QUnit Test Runner <%= stylesheet_link_tag "qunit" %> <%= stylesheet_link_tag "test_helper" %> - <%= stylesheet_link_tag "wizard" %> + <%= discourse_stylesheet_link_tag :wizard %> <%= javascript_include_tag "qunit" %> <%= javascript_include_tag "wizard/test/test_helper" %> <%= csrf_meta_tags %> diff --git a/bin/docker/boot_dev b/bin/docker/boot_dev index be31b320fd..66a038a5e7 100755 --- a/bin/docker/boot_dev +++ b/bin/docker/boot_dev @@ -1,7 +1,7 @@ #!/bin/bash SCRIPTPATH=$(cd "$(dirname "$0")"; pwd -P) SOURCE_DIR=$(cd "$SCRIPTPATH" && cd ../.. && pwd -P) -DATA_DIR=$SOURCE_DIR/tmp/postgres +DATA_DIR="$SOURCE_DIR/tmp/postgres" show_help() { cat </dev/null 2>/dev/null + if [ $? != 0 ]; then + rm $SOCKET + fi +fi + +# To enable: +# +# 1. Install socat +# 2. Add VIM_AUTOSPEC=1 to your environment +# 3. Add the following to your .vimrc +# +# function s:notify_file_change() +# let root = RailsRoot() +# let notify = root . "/bin/notify_file_change" +# if executable(notify) +# if executable('socat') +# execute "!" . notify . ' ' . expand("%:p") . " " . line(".") +# end +# end +# " redraw! +# endfunction + +# autocmd BufWritePost * silent! call s:notify_file_change() + +# What this does? +# +# bin/rake autospec will now automatically try running specs where the actual cursor is located first, then fall back to running spec file diff --git a/bin/rspec b/bin/rspec new file mode 100755 index 0000000000..0c86b5c6fa --- /dev/null +++ b/bin/rspec @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# +# This file was generated by Bundler. +# +# The application 'rspec' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require 'rubygems' +require 'bundler/setup' + +load Gem.bin_path('rspec-core', 'rspec') diff --git a/config/application.rb b/config/application.rb index 258ca0c276..4ef778ab19 100644 --- a/config/application.rb +++ b/config/application.rb @@ -78,17 +78,30 @@ module Discourse path =~ /assets\/images/ && !%w(.js .css).include?(File.extname(filename)) end] - config.assets.precompile += ['vendor.js', 'common.css', 'desktop.css', 'mobile.css', - 'admin.js', 'admin.css', 'shiny/shiny.css', 'preload-store.js.es6', - 'browser-update.js', 'embed.css', 'break_string.js', 'ember_jquery.js', - 'pretty-text-bundle.js', 'wizard.css', 'wizard-application.js', - 'wizard-vendor.js', 'plugin.js', 'plugin-third-party.js'] + config.assets.precompile += %w{ + vendor.js admin.js preload-store.js + browser-update.js break_string.js ember_jquery.js + pretty-text-bundle.js wizard-application.js + wizard-vendor.js plugin.js plugin-third-party.js + } # Precompile all available locales Dir.glob("#{config.root}/app/assets/javascripts/locales/*.js.erb").each do |file| config.assets.precompile << "locales/#{file.match(/([a-z_A-Z]+\.js)\.erb$/)[1]}" end + # out of the box sprockets 3 grabs loose files that are hanging in assets, + # the exclusion list does not include hbs so you double compile all this stuff + initializer :fix_sprockets_loose_file_searcher, after: :set_default_precompile do |app| + app.config.assets.precompile.delete(Sprockets::Railtie::LOOSE_APP_ASSETS) + start_path = ::Rails.root.join("app/assets").to_s + exclude = ['.es6', '.hbs', '.js', '.css', ''] + app.config.assets.precompile << lambda do |logical_path, filename| + filename.start_with?(start_path) && + !exclude.include?(File.extname(logical_path)) + end + end + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. config.time_zone = 'UTC' @@ -169,6 +182,8 @@ module Discourse config.relative_url_root = GlobalSetting.relative_url_root end + require_dependency 'stylesheet/manager' + config.after_initialize do # require common dependencies that are often required by plugins # in the past observers would load them as side-effects diff --git a/config/boot.rb b/config/boot.rb index afa16ad5af..6d7c45fbd6 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -9,3 +9,16 @@ require 'rubygems' ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) + +if false && ENV['BOOTSNAP'] #&& ENV['RAILS_ENV'] != 'production' + require 'bootsnap' + Bootsnap.setup( + cache_dir: 'tmp/cache', # Path to your cache + development_mode: ENV['BOOTSNAP_DEV'] == 'development', + load_path_cache: true, # Should we optimize the LOAD_PATH with a cache? + autoload_paths_cache: true, # Should we optimize ActiveSupport autoloads with cache? + disable_trace: false, # Sets `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }` + compile_cache_iseq: true, # Should compile Ruby code into ISeq cache? + compile_cache_yaml: true # Should compile YAML into a cache? + ) +end diff --git a/config/environments/development.rb b/config/environments/development.rb index a422173a67..bc345547ab 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -29,7 +29,6 @@ Discourse::Application.configure do config.active_record.migration_error = :page_load config.watchable_dirs['lib'] = [:rb] - config.sass.debug_info = false config.handlebars.precompile = false # we recommend you use mailcatcher https://github.com/sj26/mailcatcher @@ -49,6 +48,13 @@ Discourse::Application.configure do config.enable_anon_caching = false require 'rbtrace' + + require 'stylesheet/watcher' + if defined? Puma + STDERR.puts "Staring CSS change watcher" + @watcher = Stylesheet::Watcher.watch + end + if emails = GlobalSetting.developer_emails config.developer_emails = emails.split(",").map(&:downcase).map(&:strip) end diff --git a/config/environments/production.rb b/config/environments/production.rb index f73b6e2dc9..b4331f15ac 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -14,8 +14,6 @@ Discourse::Application.configure do config.assets.js_compressor = :uglifier - config.assets.css_compressor = :sass - # stuff should be pre-compiled config.assets.compile = false diff --git a/config/initializers/012-web_hook_events.rb b/config/initializers/012-web_hook_events.rb new file mode 100644 index 0000000000..93edb4e00e --- /dev/null +++ b/config/initializers/012-web_hook_events.rb @@ -0,0 +1,29 @@ +%i(topic_destroyed topic_recovered).each do |event| + DiscourseEvent.on(event) do |topic, user| + WebHook.enqueue_topic_hooks(event, topic, user) + end +end + +DiscourseEvent.on(:topic_created) do |topic, _, user| + WebHook.enqueue_topic_hooks(:topic_created, topic, user) +end + +%i(post_created + post_destroyed + post_recovered).each do |event| + + DiscourseEvent.on(event) do |post, _, user| + WebHook.enqueue_post_hooks(event, post, user) + end +end + +DiscourseEvent.on(:post_edited) do |post, topic_changed| + WebHook.enqueue_post_hooks(:post_edited, post) + WebHook.enqueue_topic_hooks(:topic_edited, post.topic) if post.is_first_post? && topic_changed +end + +%i(user_created user_approved user_updated).each do |event| + DiscourseEvent.on(event) do |user| + WebHook.enqueue_hooks(:user, user_id: user.id, event_name: event.to_s) + end +end diff --git a/config/initializers/100-sidekiq.rb b/config/initializers/100-sidekiq.rb index fee9a68ca0..515898fb01 100644 --- a/config/initializers/100-sidekiq.rb +++ b/config/initializers/100-sidekiq.rb @@ -9,13 +9,10 @@ Sidekiq.configure_server do |config| config.server_middleware do |chain| chain.add Sidekiq::Pausable - # ensure statistic middleware is included in case of a fork - chain.add Sidekiq::Statistic::Middleware end end if Sidekiq.server? - # warm up AR RailsMultisite::ConnectionManagement.each_connection do (ActiveRecord::Base.connection.tables - %w[schema_migrations]).each do |table| @@ -24,20 +21,24 @@ if Sidekiq.server? end Rails.application.config.after_initialize do - require 'scheduler/scheduler' - manager = Scheduler::Manager.new - Scheduler::Manager.discover_schedules.each do |schedule| - manager.ensure_schedule!(schedule) - end - Thread.new do - while true - begin - manager.tick - rescue => e - # the show must go on - Discourse.handle_job_exception(e, {message: "While ticking scheduling manager"}) + scheduler_hostname = ENV["UNICORN_SCHEDULER_HOSTNAME"] + + if !scheduler_hostname || scheduler_hostname == `hostname`.strip + require 'scheduler/scheduler' + manager = Scheduler::Manager.new + Scheduler::Manager.discover_schedules.each do |schedule| + manager.ensure_schedule!(schedule) + end + Thread.new do + while true + begin + manager.tick + rescue => e + # the show must go on + Discourse.handle_job_exception(e, {message: "While ticking scheduling manager"}) + end + sleep 1 end - sleep 1 end end end diff --git a/config/initializers/100-sprockets.rb b/config/initializers/100-sprockets.rb deleted file mode 100644 index f6556e220d..0000000000 --- a/config/initializers/100-sprockets.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_dependency 'sass/discourse_stylesheets' -require_dependency 'sass/discourse_sass_importer' -require_dependency 'sass/discourse_safe_sass_importer' - -DiscourseSassTemplate = Class.new(Sass::Rails::SassTemplate) do - def importer_class - DiscourseSassImporter - end -end -DiscourseScssTemplate = Class.new(DiscourseSassTemplate) do - def syntax - :scss - end -end - -Rails.application.config.assets.configure do |env| - env.register_engine '.sass', DiscourseSassTemplate - env.register_engine '.scss', DiscourseScssTemplate -end diff --git a/config/locales/client.ar.yml b/config/locales/client.ar.yml index 043c4619a8..f1fe79d99d 100644 --- a/config/locales/client.ar.yml +++ b/config/locales/client.ar.yml @@ -466,7 +466,6 @@ ar: automatic_group: مجموعة تلقائية closed_group: مجموعة مغلقة is_group_user: "أنت عضو في هذه المجموعة" - allow_membership_requests: "السماح للأعضاء لإرسال طلبات الإنضمام إلى أصحاب المجموعات (يجب أن يسمح للجميع بذكر هذه المجموعة)" membership: "العضوية" name: "الاسم" user_count: "عدد اﻷعضاء" @@ -591,7 +590,7 @@ ar: none: "(إختر خيار )" user: said: "{{username}}:" - profile: "الصفحة الشخصية" + profile: "اللاحة" mute: "كتم" edit: "تعديل التفضيلات" download_archive: @@ -719,11 +718,11 @@ ar: error: "حدث عطل أثناء تغيير البريد الإلكترونيّ. لربّما هناك من يستخدم هذا العنوان بالفعل؟" success: "لقد أرسلنا بريدا إلى هذا البريد. من فضلك اتّبع تعليمات التأكيد." change_avatar: - title: "غير صورتك الشخصية" + title: "غيّر صورة اللاحة" gravatar: "غرافاتار/Gravatar، من" gravatar_title: "غير صورتك الشخصية على موقع Gravatar's." refresh_gravatar_title: "أنعش صورة غرافاتار" - letter_based: "الصورة الافتراضية " + letter_based: "صورة لاحة يصنعها النّظام" uploaded_avatar: "تخصيص صورة" uploaded_avatar_empty: "اضافة صورة " upload_title: "رفع صورتك " @@ -731,7 +730,7 @@ ar: image_is_not_a_square: "تحذير: لقد قصصنا الصورة، إذ أن عرضها وارتفاعها لا يتطابقان." cache_notice: "لقد غيّرت صورة ملفّك بنجاح، ولكنّها قد تأخذ وقتًا حتّى تظهر بسبب خبيئة المتصفّح." change_profile_background: - title: "لون خلفية الحساب" + title: "خلفيّة اللاحة" instructions: "سيتم وضع خلفية الحساب في المنتصف بعرض 850px" change_card_background: title: "خلفية المستخدم" @@ -820,8 +819,8 @@ ar: after_2_weeks: "أُنشئت في الأسبوعين الماضيين" auto_track_topics: "تابع آليا المواضيع التي أدخلها" auto_track_options: - never: "ابداً" - immediately: "حالاً" + never: "أبدًا" + immediately: "حالًا" after_30_seconds: "بعد 30 ثانية" after_1_minute: "بعد دقيقة واحدة" after_2_minutes: "بعد دقيقتين" @@ -953,7 +952,7 @@ ar: registration_ip_address: title: "عنوان IP التسجيل" avatar: - title: "صورة الملف الشخصي" + title: "صورة اللاحة" header_title: "اللاحة، والرّسائل، والعلامات والتّفضيلات" title: title: "عنوان" @@ -1219,15 +1218,6 @@ ar: title: "أنسيت إضافة المستلمين؟" body: "حاليا هذة الرسالة مرسلة اليك فقط!" admin_options_title: "إعدادات الطّاقم الاختياريّة لهذا الموضوع" - auto_close: - label: "وقت الإغلاق التلقائي للموضوع" - error: "من فضلك أدخل قيمة صالحة." - based_on_last_post: "لاتغلق الموضوع حتى تكون آخر مشاركة بهذا القدم " - all: - examples: 'أدخل عدد الساعات (24)، أو الوقت (17:30) أو الطابع الزمني (2013-11-22 14:00).' - limited: - units: "(# من الساعات)" - examples: 'أدخل الساعة (24)' notifications: title: "إشعارات الإشارات إلى @اسمك، والردود على مواضيعك ومشاركاتك، والرسائل، وغيرها" none: "تعذّر تحميل الإشعارات الآن." @@ -1343,7 +1333,7 @@ ar: single_user: تحتوي مستخدما واحدا post: count: - label: عدد المشاركات الأدنى + label: أدنى عدد للمشاركات time: label: المُرسَلة before: قبل @@ -1531,7 +1521,6 @@ ar: '2_8': 'ستصلك إشعارات لأنك تتابع هذا القسم.' '2_4': 'ستصلك إشعارات لأنك نشرت ردا في هذا الموضوع.' '2_2': 'ستصلك إشعارات لأنك تتابع هذا الموضوع.' - '2': 'ستصلك إشعارات لأنك قرأت هذا الموضوع.' '1_2': 'سيصلك إشعار إن أشار أحد إلى @اسمك أو رد عليك.' '1': 'سيصلك إشعار إن أشار أحد إلى @اسمك أو رد عليك.' '0_7': 'لن يصلك أي إشعار يخص هذا القسم بناء على طلبك.' @@ -1567,7 +1556,6 @@ ar: open: "افتح موضوع" close: "أغلق الموضوع" multi_select: "حدد مشاركات..." - auto_close: "إغلاق تلقائي" pin: "ثبّت الموضوع..." unpin: "ألغِ تثبيت الموضوع" unarchive: "أخرج الموضوع من الأرشيف" @@ -2176,14 +2164,14 @@ ar: locked_and_archived: help: "هذا الموضوع مغلق ومؤرشف، لذا لم يعد يستقبل ردودًا ولا يمكن تغييره" unpinned: - title: "غير مثبت" - help: "هذا الموضوع غير مثبت بالنسبة لك, سيتم عرضه بالترتيب العادي" + title: "غير مثبّت" + help: "هذا الموضوع غير مثبّت لك، وسيُعرض بالترتيب العاديّ" pinned_globally: - title: "تثبيت عام" + title: "مثبّت للعموم" help: "هذا الموضوع مثبت بشكل عام, سوف يظهر في مقدمة المواضيع بآخر المشاركات وفي الأقسام الخاصة به" pinned: - title: "مثبت" - help: "هذا الموضوع مثبت لك, سوف يتم عرضه في اول القسم" + title: "مثبّت" + help: "هذا الموضوع مثبّت لك، وسيُعرض أعلى فئته" invisible: help: "هذا الموضوع غير مصنف لن يظهر في قائمة التصانيف ولايمكن الدخول عليه الابرابط مباشر." posts: "مشاركات" @@ -2253,7 +2241,7 @@ ar: categories: title: "الأقسام" title_in: "قسم - {{categoryName}}" - help: "جميع المواضيع تتبع القسم" + help: "كلّ المواضيع مجمّعة حسب الفئة" unread: title: "غير المقروءة" title_with_count: @@ -2288,7 +2276,7 @@ ar: few: "الجديدة ({{count}})" many: "الجديدة ({{count}})" other: "الجديدة ({{count}})" - help: "مواضيع تمت إضافتها مؤخراً" + help: "المواضيع المنشأة في الأيّام القليلة الماضية" posted: title: "مشاركاتي" help: "مواضيع شاركت بها " @@ -2345,7 +2333,7 @@ ar: categories: 'g، c الأقسام' top: 'g, t الأعلى' bookmarks: 'g، b العلامات' - profile: 'g, p ملف التعريف' + profile: 'g، p اللاحة' messages: 'g، m الرّسائل' navigation: title: 'التنقّل' @@ -2455,7 +2443,7 @@ ar: description: "ستتابع بشكل تلقائي جديد هذه التصنيفات. سيتم إشعارك بكل مشاركة او موضوع، بالإضافة لذلك سيتم عرض عدد المشاركات الغير مقروءة و الجديدة بجانب الموضوع." watching_first_post: title: "يُراقب فيه أول مشاركة" - description: "سنرسل لك إشعارا فقط لأول مشاركة في كل موضوع جديد في هذه المجموعة." + description: "ستستقبل إشعارًا لأوّل مشاركة في كلّ موضوع في هذا الوسم." tracking: title: "مُتابع" description: "ستتابع آليا كل مواضيع هذا الوسم. سيظهر عدّاد للمشاركات غير المقروءة والجديدة بجانب الموضوع." @@ -2464,7 +2452,7 @@ ar: description: "ستستقبل إشعارًا إن أشار أحد إلى @اسمك أو ردّ على مشاركتك." muted: title: "مكتوم" - description: "لن يتم إشعارك بأي جديد يخص هذا الموضوع ولن يظهرهذا الموضوع في تبويب المواضيع الغير مقروءة." + description: "لن تستقبل أيّ إشعار لأيّ موضوع جديد في هذا الوسم، ولن تظهر هذه المواضيع في لسان المواضيع غير المقروءة." groups: title: "مجموعات الوسوم" about: "أضف وسوما للمجموعات ليسهل عليك إدارتها." @@ -2811,32 +2799,12 @@ ar: customize: title: "تخصيص" long_title: "تخصيص الموقع" - css: "CSS" - header: "Header" - top: "Top" - footer: "تذييل " - embedded_css: "تضمين CSS" - head_tag: - text: "" - title: "رقْم HTML الذي سيُدرج قبل وسم ‎‎" - body_tag: - text: "" - title: "رقْم HTML الذي سيُدرج قبل وسم ‎‎" - override_default: "لا تضمّن ورقة الأنماط القياسيّة" - enabled: "مفعّل؟" preview: "معاينة" - undo_preview: "ازالة المعاينة" - rescue_preview: "الشكل الافتراضي" - explain_preview: "مشاهدة الموقع بهذا الشكل المخصص" - explain_undo_preview: "الرجوع الى الشكل السابق" - explain_rescue_preview: "مشاهدة الموقع بالشكل الافتراضي" save: "حفظ" new: "جديد" new_style: "تصميم جديد" import: "استيراد" - import_title: "حدد ملف او انسخ نص" delete: "حذف" - delete_confirm: "أأحذف هذا التّخصيص؟" copy: "نسخ" email_templates: title: "قالب البريد الالكتروني " @@ -2849,7 +2817,6 @@ ar: colors: title: "الألوان" long_title: "مخططات الألوان" - about: "عدّل الألوان المستخدمة في الموقع دون كتابة CSS. أضف مخطّطًا للبدء." new_name: "مخطط ألوان جديد" copy_name_prefix: "نسخة من" delete_confirm: "أأحذف مخطط الألوان هذا؟" @@ -2988,8 +2955,6 @@ ar: change_trust_level: "تغيير مستوى الثقة" change_username: "تغيير اسم المستخدم" change_site_setting: "تغيير اعدادات الموقع" - change_site_customization: "تخصيص الموقع" - delete_site_customization: "حذف هذا التخصيص؟" change_site_text: "تغيير نص الموقع." suspend_user: "حظر المستخدم" unsuspend_user: "رفع الحظر " @@ -3139,8 +3104,6 @@ ar: blocked: "محظور؟" staged: "تنظيم؟" show_admin_profile: "مدير" - edit_title: "تعديل العنوان" - save_title: "حفظ العنوان" refresh_browsers: "أجبر إنعاش المتصفح" refresh_browsers_message: "الرسالة أُرسلت إلى كل الأعضاء!" show_public_profile: "عرض الملف العام." @@ -3291,9 +3254,9 @@ ar: enabled: "يمكن تعديله" disabled: "لا يمكن تعديله" show_on_profile: - title: "عرض في الملف الشحصي العام؟" - enabled: "عرض في الملف الشخصي" - disabled: "عدم الأظهار في الملف الشخصي" + title: "أيظهر في اللاحة العامّة؟" + enabled: "يظهر في اللاحة" + disabled: "لا يظهر في اللاحة" show_on_user_card: title: "اظهارها على كرت المستخدم؟" enabled: "ظاهرة على كرت المستخدم" diff --git a/config/locales/client.bs_BA.yml b/config/locales/client.bs_BA.yml index 83f7bf5e1d..99c236b4b0 100644 --- a/config/locales/client.bs_BA.yml +++ b/config/locales/client.bs_BA.yml @@ -51,6 +51,10 @@ bs_BA: one: "1 sekunda" few: "%{count} sekundi" other: "%{count} sekundi" + x_minutes: + one: "minutu" + few: "minuta" + other: "%{count} minuta" date_month: "MMM D" date_year: "MMM 'YY" medium: @@ -364,7 +368,6 @@ bs_BA: automatic_group: Automatska grupa closed_group: Zatvorena grupa is_group_user: "Vi ste član ove grupe" - allow_membership_requests: "Dozvolite korisnicima da šalju zahtjeve za članstvom vlasnicima grupe (Zahtjeva da svako može @spomenuti grupu)" membership: "Članstvo" name: "Ime" user_count: "Broj članova" @@ -463,6 +466,7 @@ bs_BA: other_accounts: "Other accounts with this IP address" delete_other_accounts: "Izbriši %{count}" username: "korisničko ime" + trust_level: "NP" read_time: "vrijeme čitanja" topics_entered: "pogledano tema" post_count: "# postova" @@ -556,6 +560,7 @@ bs_BA: muted_users_instructions: "Odbij sve notifikacije od ovih korisnika." muted_topics_link: "Prikaži utišane teme" watched_topics_link: "Prikaži gledane teme" + tracked_topics_link: "Prikaži praćene teme" automatically_unpin_topics: "Automatski otkači temu kada dođem do dna" apps: "Aplikacije" revoke_access: "Oduzmi pristup" @@ -625,6 +630,10 @@ bs_BA: invalid: "Molimo vas unesite validnu email adresu." authenticated: "Your email has been authenticated by {{provider}}." frequency_immediately: "Slati ćemo vam e-mail obavijesti odmah na novo, ukoliko niste pročitali sadržaj koji smo vam prvobitno e-mailom poslali." + frequency: + one: "Poslaćemo ti email samo u slučaju da te nismo vidjeli u zadnjoj minuti." + few: "Poslaćemo ti email samo u slučaju da te nismo vidjeli u zadnjih {{count}} minuta." + other: "Poslaćemo ti email samo u slučaju da te nismo vidjeli u zadnjih {{count}} minuta." name: title: "Ime" instructions: "vaše puno ime (opciono)" @@ -894,6 +903,7 @@ bs_BA: not_allowed_from_ip_address: "You can't login from that IP address." admin_not_allowed_from_ip_address: "Ne možete se logirati kao admin sa te IP adrese." resend_activation_email: "Click here to send the activation email again." + change_email: "Promijeni email adresu" sent_activation_email_again: "We sent another activation email to you at {{currentEmail}}. It might take a few minutes for it to arrive; be sure to check your spam folder." to_continue: "Molimo vas ulogujte se" preferences: "Morate biti ulogovani kako bi mjenjali vaše postavke" @@ -919,6 +929,12 @@ bs_BA: github: title: "sa GitHub" message: "Authenticating with GitHub (make sure pop up blockers are not enabled)" + invites: + accept_title: "Pozivnica" + welcome_to: "Dobrodošli na %{site_name}!" + invited_by: "Pozvani ste od:" + accept_invite: "Prihvati pozivnicu" + success: "Vaš račun je napravljen i sada ste prijavljeni." emoji_set: apple_international: "Apple/International" google: "Google" @@ -983,8 +999,10 @@ bs_BA: show_preview: 'pokaži pregled »' hide_preview: '« sakri pregled' quote_post_title: "Citiraj cjeli post" + bold_label: "B" bold_title: "Bold" bold_text: "bold tekst" + italic_label: "I" italic_title: "Ukošen" italic_text: "ukošen tekst" link_title: "Link" @@ -1015,15 +1033,6 @@ bs_BA: title: "Da li ste zaboravili da dodate primaoca?" body: "Trenutno ova poruka biješe poslana samo vama!" admin_options_title: "Optional staff settings for this topic" - auto_close: - label: "Auto-close topic time:" - error: "Please enter a valid value." - based_on_last_post: "Don't close until the last post in the topic is at least this old." - all: - examples: 'Enter number of hours (24), absolute time (17:30) or timestamp (2013-11-22 14:00).' - limited: - units: "(# of hours)" - examples: 'Enter number of hours (24).' notifications: title: "obaviještenja na spomenuti @nadimak, odgovori na vaše teme i postove, privatne poruke, itd" none: "Nemate obavijesti trenutno." @@ -1228,11 +1237,20 @@ bs_BA: jump_reply_up: jump to earlier reply jump_reply_down: jump to later reply deleted: "Ova tema je obrisana" - auto_close_notice: "This topic will automatically close %{timeLeft}." - auto_close_notice_based_on_last_post: "This topic will close %{duration} after the last reply." + topic_status_update: + time: "Vrijeme:" + remove: "Ukloni tajmer" + temp_open: + title: "Privremeno otvori" + auto_reopen: + title: "Automatski otvori temu" + temp_close: + title: "Privremeno zatvori" + auto_close: + title: "Automatski zatvori temu" + label: "Vrijeme automatskog zatvaranja teme:" + error: "Molim unesite ispravnu vrijednost." auto_close_title: 'Auto-Close Settings' - auto_close_save: "Sačuvaj" - auto_close_remove: "Don't Auto-Close This Topic" timeline: back: "Nazad" back_description: "Vratite se nazad na vašu zadnju nepročitanu objavu" @@ -1262,7 +1280,6 @@ bs_BA: '2_8': 'Dobijat ćete notifikacije zato što motrite ovu temu.' '2_4': 'Dobijat ćete notifikacije zato što ste ostavili odgovor na ovoj temi.' '2_2': 'Dobijat ćete notifikacije zato što pratite ovu temu.' - '2': 'Dobijat ćete notifikacije zato što pročitao ovu temu.' '1_2': 'Dobiti ćete notifikaciju kada neko spomene tvoje @name ili odgovori na tvoj post.' '1': 'Dobiti ćete notifikaciju kada neko spomene tvoje @name ili odgovori na tvoj post.' '0_7': 'Ignorišete sve notifikacije u ovoj kategoriji.' @@ -1298,7 +1315,6 @@ bs_BA: open: "Open Topic" close: "Close Topic" multi_select: "Select Posts" - auto_close: "Auto Close" pin: "Pin Topic" unpin: "Un-Pin Topic" unarchive: "Unarchive Topic" @@ -1314,6 +1330,8 @@ bs_BA: pin_globally: "Okači temu globalno" make_banner: "Banner tema" remove_banner: "Odstrani Banner temu" + reply: + title: 'Odgovori' clear_pin: title: "Clear pin" help: "Clear the pinned status of this topic so it no longer appears at the top of your topic list" @@ -1386,6 +1404,9 @@ bs_BA: title: "Move to Existing Topic" action: "move to existing topic" error: "There was an error moving posts into that topic." + merge_posts: + title: "Spoji izabrane postove" + action: "spoji izabrane postove" change_owner: title: "Change Owner of Posts" action: "change ownership" @@ -1393,6 +1414,11 @@ bs_BA: label: "New Owner of Posts" placeholder: "username of new owner" instructions_warn: "Note that any notifications about this post will not be transferred to the new user retroactively.
    Warning: Currently, no post-dependent data is transferred over to the new user. Use with caution." + change_timestamp: + title: "Izmijeni vrijeme" + action: "izmijeni vrijeme" + invalid_timestamp: "Vrijeme ne može biti u budućnosti." + error: "Došlo je do greške prilikom izmjene vremena teme." multi_select: select: 'select' selected: 'selected ({{count}})' @@ -1402,6 +1428,7 @@ bs_BA: select_all: select all deselect_all: deselect all post: + quote_reply: "Citat" edit: "Editing {{link}} by {{replyAvatar}} {{username}}" edit_reason: "Razlog: " post_number: "post {{number}}" @@ -1451,6 +1478,7 @@ bs_BA: revert_to_regular: "Remove Staff Color" rebake: "Rebuild HTML" unhide: "Unhide" + change_owner: "Izmijeni vlasništvo" actions: flag: 'Opomena' undo: @@ -1829,32 +1857,18 @@ bs_BA: customize: title: "Customize" long_title: "Site Customizations" - css: "Stylesheet" - header: "Header" - override_default: "Do not include standard style sheet" - enabled: "Enabled?" preview: "preview" - undo_preview: "remove preview" - rescue_preview: "default style" - explain_preview: "See the site with this custom stylesheet" - explain_undo_preview: "Go back to the currently enabled custom stylesheet" - explain_rescue_preview: "See the site with the default stylesheet" save: "Save" new: "New" new_style: "New Style" delete: "Delete" - delete_confirm: "Delete this customization?" about: "Modify CSS stylesheets and HTML headers on the site. Add a customization to start." color: "Color" opacity: "Opacity" copy: "Copy" - css_html: - title: "CSS/HTML" - long_title: "CSS and HTML Customizations" colors: title: "Colors" long_title: "Color Schemes" - about: "Modify the colors used on the site without writing CSS. Add a scheme to start." new_name: "New Color Scheme" copy_name_prefix: "Copy of" delete_confirm: "Delete this color scheme?" @@ -1988,8 +2002,6 @@ bs_BA: change_trust_level: "change trust level" change_username: "zamjeni korisničko ime" change_site_setting: "change site setting" - change_site_customization: "change site customization" - delete_site_customization: "delete site customization" suspend_user: "suspend user" unsuspend_user: "unsuspend user" grant_badge: "grant badge" @@ -2069,8 +2081,6 @@ bs_BA: admin: "Admin?" blocked: "Blocked?" show_admin_profile: "Admin" - edit_title: "Edit Title" - save_title: "Save Title" refresh_browsers: "Force browser refresh" refresh_browsers_message: "Message sent to all clients!" show_public_profile: "Show Public Profile" @@ -2271,7 +2281,11 @@ bs_BA: with_time: %{username} at %{time} emoji: add: "Dodaj novi emoji" + name: "Ime" + image: "Slika" embedding: + edit: "uredi" + add_host: "Dodaj host" embed_whitelist_selector: "CSS selector za elemente koji su dozvoljeni u embeds" embed_blacklist_selector: "CSS selector za elemente koji su odstranjeni sa embeds" embed_classname_whitelist: "Dopuštena CSS class imena" diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index 79f8643c90..3ae2f256c7 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -185,7 +185,7 @@ cs: admin_title: "Administrace" flags_title: "Nahlášení" show_more: "zobrazit více" - show_help: "volby" + show_help: "možnosti" links: "Odkazy" links_lowercase: one: "odkaz" @@ -195,7 +195,7 @@ cs: guidelines: "Pokyny" privacy_policy: "Ochrana soukromí" privacy: "Soukromí" - terms_of_service: "Podmínky služby" + terms_of_service: "Podmínky používání" mobile_view: "Mobilní verze" desktop_view: "Plná verze" you: "Vy" @@ -223,7 +223,7 @@ cs: about: simple_title: "O fóru" title: "O %{title}" - stats: "Statistiky Webu" + stats: "Statistiky webu" our_admins: "Naši Admini" our_moderators: "Naši Moderátoři" stat: @@ -734,6 +734,7 @@ cs: create: "Poslat pozvánku" generate_link: "Zkopírovat odkaz na pozvánku" bulk_invite: + none: "Nikoho jsi zatím nepozval. Odešli individuální pozvánky, případně nahrej CSV soubora pozvi tak několik lidí najednou." text: "Hromadné pozvání s pomocí souboru" success: "Nahrání souboru proběhlo úspěšně. O dokončení celého procesu budete informování pomocí zprávy." password: @@ -1051,15 +1052,6 @@ cs: title: "Zapomněl/a jste přidat přijemce?" body: "Nyní tuto zprávu posíláte pouze sám/sama sobě!" admin_options_title: "Volitelné redakční nastavení tématu" - auto_close: - label: "Automaticky zavřít téma za:" - error: "Prosím zadejte platnou hodnotu." - based_on_last_post: "Neuzavírejte téma dokud poslední příspěvek v tomto tématu není alespoň takto starý." - all: - examples: 'Zadejte počet hodin (24), přesný čas (17:30) nebo časovou značku (2013-11-22 14:00).' - limited: - units: "(počet hodin)" - examples: 'Zadejte počet hodin (24).' notifications: title: "oznámení o zmínkách pomocí @name, odpovědi na vaše příspěvky a témata, zprávy, atd." none: "Notifikace nebylo možné načíst." @@ -1206,7 +1198,7 @@ cs: dismiss: "Odbýt" dismiss_read: "Odbýt všechna nepřečtená" dismiss_button: "Odbýt..." - dismiss_tooltip: "Odbýt jen nové příspěvka nebo přestat sledovat témata" + dismiss_tooltip: "Odbýt jen nové příspěvky nebo přestat sledovat témata" also_dismiss_topics: "Přestat sledovat tyto témata, takže se mi znovu nezobrazí jako nepřečtená" dismiss_new: "Odbýt nová" toggle: "hromadný výběr témat" @@ -1220,6 +1212,8 @@ cs: one: "Vybrali jste 1 téma." few: "Vybrali jste {{count}} témata." other: "Vybrali jste {{count}} témat." + change_tags: "Zaměnit štítky" + append_tags: "Přidat štítky" choose_new_tags: "Zvolte nové tagy pro témata:" changed_tags: "Tagy témat byly změněny." none: @@ -1310,16 +1304,12 @@ cs: read_more: "Chcete si přečíst další informace? {{catLink}} nebo {{latestLink}}." read_more_MF: "{ UNREAD, plural, =0 {} one { Je tu 1 nepřečtené } other { Je tu # nepřečtených } } { NEW, plural, =0 {} one { {BOTH, select, true{and } false {is } other{}} 1 nové téma} other { {BOTH, select, true{and } false {are } other{}} # nových témat} } remaining, nebo {CATEGORY, select, true {si projděte ostatní témata v kategorii {catLink}} false {{latestLink}} other {}}" browse_all_categories: Projděte všechny kategorie - view_latest_topics: si zobrazte populární témata + view_latest_topics: zobrazte si populární témata suggest_create_topic: Co takhle založit nové téma? jump_reply_up: přejít na předchozí odpověď jump_reply_down: přejít na následující odpověď deleted: "Téma bylo smazáno" - auto_close_notice: "Toto téma se automaticky zavře %{timeLeft}." - auto_close_notice_based_on_last_post: "Toto téma se uzavře za %{duration} po poslední odpovědi." auto_close_title: 'Nastavení automatického zavření' - auto_close_save: "Uložit" - auto_close_remove: "Nezavírat téma automaticky" auto_close_immediate: one: "Poslední příspěvek v témetu je již 1 hodinu starý, takže toto téma bude okamžitě uzavřeno." few: "Poslední příspěvek v témetu je již %{count} hodiny starý, takže toto téma bude okamžitě uzavřeno." @@ -1351,7 +1341,6 @@ cs: '2_8': 'Budete dostávat upozornění, protože sledujete tuto kategorii.' '2_4': 'Budete dostávat oznámení, protože jste zaslal odpověď do tohoto tématu.' '2_2': 'Budete dostávat oznámení, protože sledujete toto téma.' - '2': 'Budete dostávat oznámení, protože jste četli toto téma.' '1_2': 'Budete informováni pokud někdo zmíní vaše @jméno nebo odpoví na váš příspěvek.' '1': 'Budete informováni pokud někdo zmíní vaše @jméno nebo odpoví na váš příspěvek.' '0_7': 'Ignorujete všechna oznámení v této kategorii.' @@ -1387,7 +1376,6 @@ cs: open: "Otevřít téma" close: "Zavřít téma" multi_select: "Zvolte příspěvky…" - auto_close: "Automaticky zavřít" pin: "Připevnit téma" unpin: "Odstranit připevnění" unarchive: "Navrátit z archivu" @@ -1403,6 +1391,8 @@ cs: pin_globally: "Připnout téma globálně" make_banner: "Banner Topic" remove_banner: "Remove Banner Topic" + reply: + title: 'Odpovědět' clear_pin: title: "Odstranit připnutí" help: "Odebere připnutí tohoto tématu, takže se již nebude zobrazovat na vrcholu seznamu témat" @@ -1959,18 +1949,18 @@ cs: unread: title: "Nepřečtená" title_with_count: - one: "Nepřečtená ({{1}})" + one: "Nepřečtená (1)" few: "Nepřečtená ({{count}})" other: "Nepřečtená ({{count}})" - help: "témata. která sledujete nebo hlídáte, s nepřečtenými příspěvky" + help: "témata, která sledujete nebo hlídáte, s nepřečtenými příspěvky" lower_title_with_count: one: "{{count}} nepřečtené" few: "{{count}} nepřečtených" other: "{{count}} nepřečtených" new: lower_title_with_count: - one: "{{count}} nové" - few: "{{count}} nových" + one: "1 nové" + few: "{{count}} nová" other: "{{count}} nových" lower_title: "nové" title: "Nová" @@ -2456,31 +2446,12 @@ cs: customize: title: "Přizpůsobení" long_title: "Přizpůsobení webu" - css: "CSS" - header: "header" - top: "Vršek" - footer: "Patička" - head_tag: - text: "" - title: "HTML které bude vloženo před HTML tag" - body_tag: - text: "" - title: "HTML které bude vloženo před HTML tag" - override_default: "Přetížit výchozí?" - enabled: "Zapnuto?" preview: "náhled" - undo_preview: "odstranit náhled" - rescue_preview: "výchozí styl" - explain_preview: "Náhled stránky s vlastním stylesheetem." - explain_undo_preview: "Vrátit se k aktuálnímu použitému vlastnímu stylesheetu." - explain_rescue_preview: "Zobrazit web s výchozím stylesheetem." save: "Uložit" new: "Nové" new_style: "Nový styl" import: "Import" - import_title: "Vyberte soubor nebo vložte text" delete: "Smazat" - delete_confirm: "Smazat toto přizpůsobení?" about: "Změn CSS styly a hlavičky HTML na stránkách. Přidej přizpůsobení na začátek." color: "Barva" opacity: "Neprůhlednost" @@ -2493,13 +2464,9 @@ cs: none_selected: "Pro začátek editace zvolte šablonu emailu." revert: "Vrátit změny" revert_confirm: "Opravdu chcete vrátit změny?" - css_html: - title: "CSS/HTML" - long_title: "Přizpůsobení CSS a HTML" colors: title: "Barvy" long_title: "Barevná schémata" - about: "Změn barvy použité na stránkách bez psaní CSS. Přidej schéma na začátek." new_name: "Nové barevné schéma" copy_name_prefix: "Kopie" delete_confirm: "Chcete smazat toto barevné schéma?" @@ -2633,8 +2600,6 @@ cs: change_trust_level: "z. důvěryhodnosti" change_username: "Změnit uživatelské jméno" change_site_setting: "změna nastavení" - change_site_customization: "změna přizpůsobení" - delete_site_customization: "odstranit přizpůsobení" change_site_text: "změnit text webu" suspend_user: "suspendovat uživatele" unsuspend_user: "zrušit suspendování" @@ -2756,8 +2721,6 @@ cs: admin: "Administrátor?" blocked: "Zablokovaný?" show_admin_profile: "Administrace" - edit_title: "Upravit titul" - save_title: "Uložit nadpis" refresh_browsers: "Vynutit obnovení prohlížeče" refresh_browsers_message: "Zpráva odeslána všem klientům!" show_public_profile: "Zobrazit veřejný profil" @@ -2776,7 +2739,7 @@ cs: activity: Aktivita like_count: Rozdaných / obdržených 'líbí se' last_100_days: 'Za posledních 100 dní' - private_topics_count: Počet soukromách témat + private_topics_count: Počet soukromých témat posts_read_count: Přečteno příspěvků post_count: Vytvořeno příspěvků topics_entered: Témat zobrazeno diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index 86ee10c234..12e1992e80 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -135,6 +135,9 @@ da: visible: enabled: 'listet %{when}' disabled: 'aflistet %{when}' + banner: + enabled: 'lavede dette til et banner %{when}. Emnet bliver vist på toppen af alle sider indtil det bliver lukket af brugeren.' + disabled: 'fjernede banneret %{when}. Det vil ikke længere vises i toppen af alle sider.' topic_admin_menu: "administrationshandlinger på emne" emails_are_disabled: "Alle udgående emails er blevet deaktiveret globalt af en administrator. Ingen emailnotifikationer af nogen slags vil blive sendt." bootstrap_mode_enabled: "For at gøre det nemmere at lancere dit nye website, er du i bootstrap tilstand. Alle nye brugere vil blive tildelt niveau 1 og daglige email updateringer vil være slået til. Dette vil blive automatisk slået fra når total antal brugere overskrider %{min_users} brugere." @@ -165,7 +168,7 @@ da: age: "Alder" joined: "Tilmeldt" admin_title: "Admin" - flags_title: "Flag" + flags_title: "Flagmarkeringer" show_more: "vis mere" show_help: "indstillinger" links: "Links" @@ -346,7 +349,9 @@ da: title: "Medlemskabsanmodning" body: "Jeg vil gerne ansøge om medlemskab i @%{groupName}." name_placeholder: "Gruppe navn, ingen mellemrum, samme som reglen for brugernavn" + public: "Tillad at brugere frit kan tilmelde sig/forlade gruppen (Kræver at gruppen er synlig)" empty: + posts: "Der er ingen indlæg skrevet af medlemmer i denne gruppe." members: "Der er ingen medlemmer i denne gruppe." mentions: "Denne gruppe bliver ikke nævnt nogen steder." messages: "Der er ingen beskeder til denne gruppe." @@ -359,12 +364,21 @@ da: automatic_group: Automatisk gruppe closed_group: Lukket gruppe is_group_user: "Du er medlem af denne gruppe" + allow_membership_requests: "Tillad at brugere sender medlemsanmodninger til gruppeejere" + membership: "Medlemskab" + name: "Navn" + user_count: "Antal medlemmer" + bio: "Om Grupper" selector_placeholder: "Tilføj medlemmer" owner: "ejer" visible: "Gruppen er synlige for alle brugere" + index: + title: "Grupper" + empty: "Der er ingen synlige grupper" title: one: "gruppe" other: "grupper" + activity: "Aktivitet" members: "Medlemmer" topics: "Emner" posts: "Indlæg" @@ -396,6 +410,11 @@ da: muted: title: "Tavs" description: "Du får aldrig beskeder om nye emner i denne gruppe." + flair_url_placeholder: "(Valgfrit) Billed-URL eller Font Awesome klasse" + flair_bg_color_placeholder: "(Valgfrit) Hex farveværdi" + flair_color_placeholder: "(Valgfrit) Hex farveværdi" + flair_preview_icon: "Forhåndsvisning af ikonet" + flair_preview_image: "Forhåndsvisning af billede" user_action_groups: '1': "Likes givet" '2': "Likes modtaget" @@ -457,6 +476,11 @@ da: profile: "Profil" mute: "Mute" edit: "Redigér indstillinger" + download_archive: + button_text: "Download Mine Indlæg" + confirm: "Er du sikker på, at du vil downloade dine indlæg?" + success: "Downloaden er startet. Du vil blive notificeret via en besked, når processen er færdig." + rate_limit_error: "Indlæg kan downloades en gang om dagen, prøv igen i morgen." new_private_message: "Ny Besked" private_message: "Besked" private_messages: "Beskeder" @@ -480,6 +504,7 @@ da: each_browser_note: "Bemærk: Du skal ændre indstillingen i alle dine browsere." dismiss_notifications: "Afvis alle" dismiss_notifications_tooltip: "Marker alle ulæste notifikationer som læst" + first_notification: "Din første notifikation! Vælg den for at begynde." disable_jump_reply: "Ikke hop til mit indlæg efter jeg svarer" dynamic_favicon: "Vis nyt / opdateret emnetal på browserikon" external_links_in_new_tab: "Åbn alle eksterne links i en ny fane" @@ -502,6 +527,7 @@ da: Fravalgte / 'muted' emner og kategorier fer ikke inkluderet i disse mails. daily: "Send daglige opdateringer" individual: "Send en email for hvert nyt indlæg" + individual_no_echo: "Send en e-mail for hver nyt indlæg bortset fra mine egne" many_per_day: "Send mig en email for hvert nyt indlæg (omkring {{dailyEmailEstimate}} per dag)" few_per_day: "Send mig en email or hvert nyt indlæg (cirka 2 om dagen)" tag_settings: "Tags" @@ -532,6 +558,7 @@ da: muted_users_instructions: "Undertryk alle notifikationer fra disse brugere." muted_topics_link: "Vis mute emner" watched_topics_link: "Vis sete emner" + tracked_topics_link: "Vis fulgte emner" automatically_unpin_topics: "Automatisk stop med at følge emner, når jeg når til bunden." apps: "Apps" revoke_access: "Tilbagekald adgang" @@ -560,6 +587,8 @@ da: error: "(fejl)" action: "Send e-mail til nulstilling af adgangskode" set_password: "Skriv password" + choose_new: "Vælg et nyt kodeord" + choose: "Vælg et kodeord" change_about: title: "Skift “Om mig”" error: "Der opstod en fejl i ændringen af denne værdi." @@ -594,6 +623,7 @@ da: instructions: "Baggrunds billeder vil blive centreret og have en standard bredde på 590px." email: title: "E-mail" + instructions: "vises aldrig offentligt" ok: "Vi vil sende dig en bekræftelses email" invalid: "Indtast venligst en gyldig email adresse" authenticated: "Din email er blevet bekræftet af {{provider}}" @@ -603,14 +633,17 @@ da: other: "Vi sender dig kun email. hvis vi ikke har set dig i de sidste {{count}} minutter." name: title: "Navn" + instructions: "Dit fulde navn (valgfrit)" instructions_required: "Dit fulde navn" too_short: "Dit navn er for kort" ok: "Dit navn ser fint ud" username: title: "Brugernavn" + instructions: "unikt, ingen mellemrum, kort" short_instructions: "Folk kan benævne dig som @{{username}}" available: "Dit brugernavn er tilgængeligt" not_available: "Ikke ledigt. Prøv {{suggestion}}?" + not_available_no_suggestion: "Ikke tilgængelig" too_short: "Dit brugernavn er for kort" too_long: "Dit brugernavn er for langt" checking: "Kontrollerer om brugernavnet er ledigt…" @@ -676,6 +709,7 @@ da: after_4_minutes: "efter 4 minutter" after_5_minutes: "efter 5 minutter" after_10_minutes: "efter 10 minutter" + notification_level_when_replying: "Når jeg skriver i et emne, sæt emnet som" invited: search: "tast for at søge invitationer…" title: "Invitationer" @@ -706,9 +740,13 @@ da: account_age_days: "Kontoens alder i dage" create: "Send en invitation" generate_link: "Kopier invitations-link" + link_generated: "Invitiationslinket blev genereret!" + valid_for: "Imvitationslinket er kun gyldigt for emailadressen: %{email}" bulk_invite: + none: "Du har ikke inviteret noget endnu. Send individuelle invitationer eller inviter mange på en gang ved at uploade en CSV-fil." text: "Masse invitering fra en fil" success: "Fil uploaded successfuldt, du vil blive meddelt via en beskede når processen er fuldendt." + error: "Desværre, filen skal være CSV format." password: title: "Adgangskode" too_short: "Din adgangskode er for kort." @@ -716,6 +754,7 @@ da: same_as_username: "Dit password er det samme som dit brugernavn." same_as_email: "Dit password er det samme som din email adresse." ok: "Din adgangskode ser fin ud." + instructions: "mindst %{count} tegn" summary: title: "Statistik" stats: "Statistik" @@ -762,7 +801,7 @@ da: registration_ip_address: title: "Registrerings IP adresse" avatar: - title: "Profil Billede" + title: "Profilbillede" header_title: "profil, beskeder, bogmærker og indstillinger." title: title: "Titel" @@ -804,7 +843,13 @@ da: too_few_topics_and_posts_notice: "Lad os få startet denne diskussion! Der er i øjeblikket %{currentTopics} / %{requiredTopics} emner og %{currentPosts} / %{requiredPosts} indlæg. Nye besøgende har brug for samtaler at læse og svare på." too_few_topics_notice: "Lad os få startet denne diskussion ! Der er i øjeblikket %{currentTopics} / %{requiredTopics} emner. Nye besøgende har brug for samtaler at læse og svare på." too_few_posts_notice: "Lad os få startet denne diskussion ! Der er i øjeblikket %{currentPosts} / %{requiredPosts} indlæg. Nye besøgende har brug for samtaler at læse og svare på." + logs_error_rate_notice: + rate: + one: "1 fejl/%{duration}" + other: "%{count} fejl/%{duration}" learn_more: "Læs mere…" + all_time: 'i alt' + all_time_desc: 'emner oprettet i alt' year: 'år' year_desc: 'Indlæg oprettet i de seneste 365 dage' month: 'måned' @@ -877,12 +922,16 @@ da: logging_in: "Logger ind..." or: "Eller" authenticating: "Logger ind…" + awaiting_activation: "Din konto mangler at blive aktiveret. Brug “Jeg har glemt min adgangskode”-linket for at få en ny aktiverings-mail." awaiting_approval: "Din konto er ikke blevet godkendt af en moderator endnu. Du får en e-mail når den bliver godkendt." requires_invite: "Beklager, det kræve en invitation at blive medlem af dette forum." not_activated: "Du kan ikke logge ind endnu. Vi har tidligere sendt en aktiverings-e-mail til dig på {{sentTo}}. Følg venligst instruktionerne i den e-mail for at aktivere din konto." not_allowed_from_ip_address: "Du kan ikke logge ind fra den IP adresse." admin_not_allowed_from_ip_address: "Du kan ikke logge på som administrator fra denne IP adresse." resend_activation_email: "Klik her for at sende aktiverings-e-mail’en igen." + resend_title: "Gensend Aktiverings-e-mail" + change_email: "Skift e-mailadresse" + submit_new_email: "Opdater e-mailadresse" sent_activation_email_again: "Vi har sendt endnu en aktiverings-e-mail til dig på {{currentEmail}}. Det kan tage nogen få minutter før den når frem; kontrollér også din spam-mappe." to_continue: "Log venligst ind" preferences: "Du skal være logget ind for at ændre præferencer." @@ -908,14 +957,25 @@ da: github: title: "med GitHub" message: "Logger ind med GitHub (kontrollér at pop-op-blokering ikke er aktiv)" + invites: + accept_title: "Invitation" + welcome_to: "Velkommen til %{site_name}!" + invited_by: "Du blev inviteret af:" + your_email: "E-mail-adressen knyttet til kontoen er %{email}." + accept_invite: "Accepter Invitation" + success: "Din konto er blevet oprettet, og du er nu logget ind." + password_label: "Angiv Kodeord (valgfrit)" + password_reset: + continue: "Fortsæt til %{site_name}" emoji_set: apple_international: "Apple/International" google: "Google" twitter: "Twitter" - emoji_one: "Emoji One" + emoji_one: "Første Emoji" win10: "Win10" category_page_style: categories_only: "Kun Kategorier" + categories_with_featured_topics: "Kategorier med Fremhævede Emner" categories_and_latest_topics: "Kategorier og Seneste Emner" shortcut_modifier_key: shift: 'Skift' @@ -936,6 +996,9 @@ da: saved_local_draft_tip: "gemt lokalt" similar_topics: "Dit emne minder om…" drafts_offline: "kladder offline" + group_mentioned: + one: "Ved at nævne {{group}} sender du en notifikation til 1 person - vil du fortsætte?" + other: "Ved at nævne {{group}} sender du en notifikation til {{count}} personer - vil du fortsætte?" error: title_missing: "Titlen er påkrævet" title_too_short: "Titlen skal være på mindst {{min}} tegn" @@ -954,6 +1017,7 @@ da: title: "Eller tryk Ctrl+Enter" users_placeholder: "Tilføj bruger" title_placeholder: "Hvad handler diskussionen om i korte træk?" + title_or_link_placeholder: "Skriv titlen eller indsæt et link her" edit_reason_placeholder: "hvorfor redigerer du?" show_edit_reason: "(tilføj en begrundelse for ændringen)" reply_placeholder: "Skriv her. Brug Markdown, BBCode eller HTML til at formattere. Træk eller indsæt billeder." @@ -999,15 +1063,6 @@ da: title: "Glemte du at tilføje modtagere?" body: "Lige nu bliver denne besked kun sendt til dig selv!" admin_options_title: "Valgfrie staff-indstillinger for dette emne" - auto_close: - label: "Tidspunkt for automatisk lukning af emne:" - error: "Indtast venligst en gyldig værdi" - based_on_last_post: "Luk ikke før det seneste indlæg i emnet er mindst så gammel." - all: - examples: 'Indtast et antal timer (24), et bestemt tidspunkt (17:30) eller et tidsstempel (2013-11-22 14:00).' - limited: - units: "(# timer)" - examples: 'Indtast antal timer (24).' notifications: title: "notifikationer ved @navns nævnelse, svar på dine indlæg og emner, beskeder, mv." none: "Ikke i stand til at indlæse notifikationer for tiden." @@ -1074,6 +1129,7 @@ da: sort_by: "Sorter efter" relevance: "Relevans" latest_post: "Seneste indlæg" + latest_topic: "Seneste Emne" most_viewed: "Mest sete" most_liked: "Mest likede" select_all: "Vælg alle" @@ -1092,13 +1148,51 @@ da: category: "Søg i #{{category}} kategorien" topic: "Søg i dette emne" private_messages: "Søg i beskeder" + advanced: + title: Avanceret Søgning + posted_by: + label: Skrevet af + in_category: + label: I Kategorien + in_group: + label: I Gruppen + with_badge: + label: Med Badge + with_tags: + label: Med Tags + filters: + posted: Jeg skrev i + watching: Jeg overvåger + tracking: Jeg følger + private: er i mine beskeder + bookmarks: Jeg har bogmærket + first: er det allerførste indlæg + pinned: er fastgjort + unpinned: er ikke fastgjort + seen: Jeg har læst + unseen: Jeg har ikke læst + wiki: er wiki + statuses: + open: er åbne + closed: er lukkede + archived: er arkiverede + noreplies: har ingen svar + single_user: indeholder en enkelt bruger + post: + time: + label: Sendt + before: før + after: efter hamburger_menu: "gå til en anden emneliste eller kategori" new_item: "ny" go_back: 'gå tilbage' not_logged_in_user: 'bruger side, med oversigt over aktivitet og indstillinger' current_user: 'gå til brugerside' topics: + new_messages_marker: "sidste besøg" bulk: + select_all: "Vælg alle" + clear_all: "Ryd alle" unlist_topics: "Fjern emner fra liste" reset_read: "Nulstil \"læst\"" delete: "Slet emner" @@ -1118,7 +1212,10 @@ da: selected: one: "Du har valgt 1 indlæg." other: "Du har valgt {{count}} indlæg." + change_tags: "Erstat Tags" + append_tags: "Tilføj Tags" choose_new_tags: "Vælg nye tags for dette emne" + choose_append_tags: "Vælg nye tags til at tilføje emnet:" changed_tags: "Tags for dette emne blev ændret" none: unread: "Du har ingen ulæste emner." @@ -1149,6 +1246,9 @@ da: unsubscribe: stop_notifications: "Du vil nu modtage færre notifikationer for {{title}}" change_notification_state: "Din nuværende notifikationstilstand er" + filter_to: + one: "1 indlæg i emnet" + other: "{{count}} indlæg i emnet" create: 'Nyt emne' create_long: 'Opret et nyt emne i debatten' private_message: 'Start en besked' @@ -1203,11 +1303,32 @@ da: jump_reply_up: hop til tidligere svar jump_reply_down: hop til senere svar deleted: "Emnet er blevet slettet" - auto_close_notice: "Dette emne lukker automatisk %{timeLeft}." - auto_close_notice_based_on_last_post: "Dette emne vil lukke %{duration} efter det sidste svar." + topic_status_update: + publish_to: "Udgiv I:" + auto_update_input: + limited: + units: "(# timer)" + examples: 'Indtast antal timer (24).' + all: + examples: 'Indtast et antal timer (24), et bestemt tidspunkt (17:30) eller et tidsstempel (2013-11-22 14:00).' + publish_to_category: + title: "Planlæg Udgivelse" + temp_open: + title: "Åben Midlertidigt" + auto_reopen: + title: "Åben Automatisk Emnet" + temp_close: + title: "Luk Midlertidigt" + auto_close: + title: "Luk Automatisk Emnet" + error: "Indtast venligst en gyldig værdi." + based_on_last_post: "Luk ikke før det seneste indlæg i emnet er mindst så gammel." + status_update_notice: + auto_open: "Emnet åbner automatisk %{timeLeft}." + auto_close: "Emnet lukker automatisk %{timeLeft}." + auto_publish_to_category: "Emnet vil blive udgivet i #%{categoryName} %{timeLeft}." + auto_close_based_on_last_post: "Emnet vil lukke %{duration} efter det sidste svar." auto_close_title: 'Indstillinger for automatisk lukning' - auto_close_save: "Gem" - auto_close_remove: "Luk ikke dette emne automatisk" auto_close_immediate: one: "Seneste indlæg i emnet er allerede 1 time gammelt så emnet bliver lukket med det samme." other: "Seneste indlæg i emnet er allerede %{hours} timer gammelt så emnet bliver lukket med det samme." @@ -1221,6 +1342,8 @@ da: go_bottom: "bund" go: "start" jump_bottom: "Hop til sidste indlæg" + jump_prompt: "spring til..." + jump_prompt_of: "af %{count} indlæg" jump_prompt_long: "Hvilket indlæg vil du gerne springe til?" jump_bottom_with_number: "hop til indlæg %{post_number}" total: antal indlæg @@ -1238,7 +1361,7 @@ da: '2_8': 'Du får notifikationer fordi du følger dette kategori.' '2_4': 'Du får notifikationer fordi du har besvaret dette emne.' '2_2': 'Du får notifikationer fordi du følger dette emne.' - '2': 'Du får notifikationer fordi du har læst dette emne.' + '2': 'Du modtager notifikationer, fordi du læste dette emne.' '1_2': 'Du vil modtage en notifikation hvis nogen nævner dit @name eller svarer dig.' '1': 'Du vil modtage en notifikation hvis nogen nævner dit @name eller svarer dig.' '0_7': 'Du ignorerer alle notifikationer i denne kategori.' @@ -1274,7 +1397,6 @@ da: open: "Åbn emne" close: "Luk emne" multi_select: "Vælg indlæg..." - auto_close: "Luk automatisk..." pin: "Fastgør Emne..." unpin: "Fjern fastgøring af Emne..." unarchive: "Gendan emne fra arkiv" @@ -1290,12 +1412,18 @@ da: pin_globally: "Fastgør emne globalt" make_banner: "Gør emnet til en banner" remove_banner: "Emnet skal ikke være banner længere" + reply: + title: 'Svar' + help: 'start på et svar til dette emne' clear_pin: title: "Fjern tegnestift" help: "Fjern tegnestiften på dette emne så det ikke længere vises i toppen af emnelisten" share: title: 'Del' help: 'del et link til dette emne' + print: + title: 'Print' + help: 'Åben en printervenlig udgave at emnet' flag_topic: title: 'Rapportér indlæg' help: 'gør moderator opmærksom på dette indlæg' @@ -1408,11 +1536,14 @@ da: post: reply: " {{replyAvatar}} {{usernameLink}}" reply_topic: " {{link}}" + quote_reply: "Citér" edit: "Redigerer {{link}} {{replyAvatar}} {{username}}" edit_reason: "Reason: " post_number: "indlæg {{number}}" + wiki_last_edited_on: "wiki sidst redigeret den" last_edited_on: "indlæg sidst redigeret den" reply_as_new_topic: "Svar som linket emne" + reply_as_new_private_message: "Svar med ny besked til de samme modtagere" continue_discussion: "Fortsætter debatten fra {{postLink}}:" follow_quote: "gå til det citerede indlæg" show_full: "Vis hele emnet" @@ -1455,6 +1586,8 @@ da: via_email: "dette indlæg blev oprettet via email" via_auto_generated_email: "dette indlæg blev oprettet af en auto genereret e-mail" whisper: "dette indlæg er en privat hvisken for moderatorer" + wiki: + about: "indlægget er en wiki" archetypes: save: 'Gem indstillinger' few_likes_left: "Tak fordi du liker! Du har kun få likes tilbage i dag." @@ -1581,14 +1714,24 @@ da: hide: "Skjul udgave" show: "Vis udgave" revert: "Gå tilbage til denne udgave" + edit_wiki: "Ret Wiki" + edit_post: "Ret Indlæg" comparing_previous_to_current_out_of_total: "{{previous}} {{current}} / {{total}}" displays: inline: title: "Vis det renderede output med tilføjelser og ændringer indlejret" + button: 'HTML' side_by_side: title: "Vis de renderede output-diffs ved siden af hinanden" + button: 'HTML' side_by_side_markdown: title: "Vis forskellen på den rå kildekode side om side" + raw_email: + displays: + text_part: + button: 'Tekst' + html_part: + button: 'HTML' category: can: 'kan… ' none: '(ingen kategori)' @@ -1605,6 +1748,7 @@ da: tags_allowed_tag_groups: "Mærke grupper der kun kan bruges i denne kategori:" tags_placeholder: "(Valgfri) liste af tiladte mærker" tag_groups_placeholder: "(Valgfri) liste af tiladte mærke grupper" + topic_featured_link_allowed: "Tillad fremhævede links i denne kategori" delete: 'Slet kategori' create: 'Ny kategori' create_long: 'Opret en ny kategori' @@ -1639,6 +1783,7 @@ da: email_in_disabled: "Nye emner via email er deaktiveret i Site opsætning. For at aktivere oprettelse af nye emner via email," email_in_disabled_click: 'aktiver "email ind" indstilligen.' suppress_from_homepage: "Undertryk denne kategori fra hjemmesiden" + all_topics_wiki: "Gør som standard nye emner til wikis" allow_badges_label: "Tillad af badges bliver tildelt i denne kategori" edit_permissions: "Redigér tilladelser" add_permission: "Tilføj tilladelse" @@ -1664,6 +1809,22 @@ da: muted: title: "Ignoreret" description: "Du vil aldrig få besked om noget om nye emner i kategorierne og de vises heller ikke i seneste." + sort_options: + default: "standard" + likes: "Likes" + views: "Visninger" + posts: "Indlæg" + activity: "Aktivitet" + posters: "Forfattere" + category: "Kategori" + created: "Oprettet" + sort_ascending: 'Stigende' + sort_descending: 'Faldende' + subcategory_list_styles: + rows: "Rækker" + rows_with_featured_topics: "Rækker med fremhævede emner" + boxes: "Bokse" + boxes_with_featured_topics: "Bokse med fremhævede emner" flagging: title: 'Tak fordi du hjælper med at holde vores forum civiliseret!' action: 'Flag indlæg' @@ -1685,6 +1846,16 @@ da: spam: "Det er spam" custom_placeholder_notify_user: "Vær præcis, vær kontruktiv og vær altid venlig." custom_placeholder_notify_moderators: "Lad os vide præcis hvad du er bekymret over og giv relevante links og eksempler hvor det er muligt." + custom_message: + at_least: + one: "indtast mindst 1 tegn" + other: "indtast mindst {{count}} tegn" + more: + one: "1 mere..." + other: "{{count}} mere..." + left: + one: "1 tilbage" + other: "{{count}} tilbage" flagging_topic: title: "Tak fordi du hjælper med at holde vores forum civiliseret!" action: "Rapporter emne" @@ -1699,6 +1870,9 @@ da: other: "%{count} klik" post_links: about: "udvid flere links for dette indlæg" + title: + one: "1 mere" + other: "%{count} flere" topic_statuses: warning: help: "Dette er en officiel advarsel." @@ -1735,6 +1909,9 @@ da: one: "visning" other: "visninger" replies: "Svar" + views_long: + one: "emnet er læst 1 gang" + other: "emnet er læst {{number}} gange" activity: "Aktivitet" likes: "Likes" likes_lowercase: @@ -1749,6 +1926,7 @@ da: history: "Historik" changed_by: "af {{author}}" raw_email: + title: "Indgående e-mail" not_available: "Ikke tilgængelig!" categories_list: "Kategorioversigt" filters: @@ -1867,18 +2045,61 @@ da: log_out: 'shift+z shift+z Log ud' actions: title: 'Handlinger' + pin_unpin_topic: 'shift+p Fastgør/Frigør emne' + share_topic: 'shift+s Del emne' + share_post: 's Del indlæg' + reply_topic: 'shift+r Besvar emne' + reply_post: 'r Besvar indlæg' + quote_post: 'q Citer indlæg' + like: 'l Like indlæg' + flag: '! Flag indlæg' + bookmark: 'b Bogmærk indlæg' + edit: 'e Redigér indlæg' + delete: 'd Slet indlæg' + mark_muted: 'Ignorer emnet' + mark_tracking: 'm, t Følg emne' + mark_watching: 'm, w Hold øje med emne' + print: 'ctrl+p Print emne' badges: + earned_n_times: + one: "Blev tildelt dette badge 1 gang" + other: "Blev tildelt dette badge %{count} gange" + granted_on: "Tildelt %{date}" + others_count: "Andre med dette badge (%{count})" title: Badges allow_title: "tilgængelig titel" multiple_grant: "tildelt flere gange" + badge_count: + one: "1 Badge" + other: "%{count} Badges" + more_badges: + one: "+1 Mere" + other: "+%{count} Flere" + granted: + one: "1 tildelt" + other: "%{count} tildelt" + select_badge_for_title: Vælg en badge, du vil bruge som din titel none: "" badge_grouping: getting_started: name: Sådan kommer du i gang community: name: Fællesskab + trust_level: + name: Tillidsniveau other: name: Andet + posting: + name: Sender + google_search: | +

    Søg med Google

    +

    + + + + + +

    tagging: all_tags: "Alle tags" selector_all_tags: "alle tags" @@ -1912,6 +2133,8 @@ da: description: "Du vil automatisk følge alle emner i dette tag. Et antal af ulæste og nye indlæg vil vises ved siden af emnet." regular: description: "Du vil blive notificeret if nogen nævner dit You will be notified if someone mentions your @name or replies to your post." + muted: + title: "Lydløs" groups: title: "Tag grupper" about: "Tilføj tags til grupper for nemmere håndtering." @@ -1994,6 +2217,7 @@ da: 30_days_ago: "30 dage siden" all: "Alle" view_table: "tabel" + view_graph: "graf" refresh_report: "Genopfrisk rapporten" start_date: "Start dato" end_date: "Slut dato" @@ -2108,10 +2332,23 @@ da: all_users: "Alle brugere" note_html: "Hold denne nøgle hemmelig, alle brugere som har den kan oprette vilkårlige indlæg, som enhver bruger." web_hooks: + title: "Webhooks" + none: "Der er ingen webhooks i øjeblikket" + new: "Ny Webhook" + create: "Opret" + save: "Gem" + destroy: "Slet" + description: "Beskrivelse" + content_type: "Indholdstype" secret: "Hemmelig" wildcard_event: "Send mig alt" individual_event: "Vælg individuelle events" active: "Aktiv" + delivery_status: + inactive: "Inaktiv" + events: + ping: "Ping" + timestamp: "Oprettet" plugins: title: "Plugins" installed: "Installerede Plugins" @@ -2193,32 +2430,12 @@ da: customize: title: "Tilpasning" long_title: "Tilpasning af site" - css: "CSS" - header: "Header" - top: "Top" - footer: "Bund" - embedded_css: "Indlejret CSS" - head_tag: - text: "" - title: "HTML som indsættes før -tagget" - body_tag: - text: "" - title: "HTML som indsættes før -tagget" - override_default: "Inkludér ikke standard stylesheet" - enabled: "Aktiveret?" preview: "forhåndsvisning" - undo_preview: "skjul forhåndsvisning" - rescue_preview: "standard stil" - explain_preview: "Se sitet med dette stylesheet" - explain_undo_preview: "Gå tilbage til det aktuelle ændrede stylesheet" - explain_rescue_preview: "Se sitet med standard stylesheet" save: "Gem" new: "Ny" new_style: "Ny style" import: "Importer" - import_title: "Vælg en fil eller indsæt tekst" delete: "Slet" - delete_confirm: "Slet denne tilpasning?" about: "Modificer CSS stylesheets og HTML headere på sitet. Tilføj en tilpasning for at starte." color: "Farve" opacity: "Gennemsigtighed" @@ -2231,13 +2448,9 @@ da: none_selected: "Vælg en email-skabelon for at begynde at redigere." revert: "Rul ændringer tilbage" revert_confirm: "Er du sikker på, at du vil rulle ændringerne tilbage?" - css_html: - title: "CSS, HTML" - long_title: "CSS og HTML tilpasninger" colors: title: "Farver" long_title: "Farve temaer" - about: "Modificer farverne der bliver brugt på sitet uden at skrive CSS. Tilføj et tema for at begynde." new_name: "Nyt farve tema" copy_name_prefix: "Kopi af" delete_confirm: "Slet dette farvetema?" @@ -2299,6 +2512,7 @@ da: delivery_method: "Leveringsmetode" preview_digest_desc: "Forhåndsvis indholdet af de opsamlings-emails der sendes til inaktive brugere" refresh: "Opdatér" + send_digest: "Send" format: "Format" html: "html" text: "text" @@ -2352,7 +2566,7 @@ da: do_nothing: "gør intet" staff_actions: title: "Handlinger" - instructions: "Klik på brugernavne og handlinger for at filtrere listen. Klik på profil billederne for at gå til bruger siderne." + instructions: "Klik på brugernavne og handlinger for at filtrere listen. Klik på profilbillederne for at gå til brugersiderne." clear_filters: "Vis alt" staff_user: "Bruger" target_user: "Bruger" @@ -2372,8 +2586,6 @@ da: change_trust_level: "skift tillidsniveau" change_username: "skift brugernavn" change_site_setting: "skift indstillinger for site" - change_site_customization: "skift tilpasning af site" - delete_site_customization: "slet tilpasning af site" change_site_text: "skift tekst for site" suspend_user: "suspendér user" unsuspend_user: "ophæv suspendering af bruger" @@ -2394,9 +2606,16 @@ da: revoke_admin: "fjern admin" grant_moderation: "tildel moderation" revoke_moderation: "fjern moderation" + backup_create: "opret backup" deleted_tag: "slettet tag" renamed_tag: "omdøbt tag" revoke_email: "tilbagekald email" + lock_trust_level: "lås tillidsniveau" + unlock_trust_level: "lås tillidsniveau op" + activate_user: "Aktivér bruger" + deactivate_user: "Deaktivér bruger" + backup_download: "download backup" + backup_destroy: "slet backup" screened_emails: title: "Blokerede e-mails" description: "Følgende e-mail-adresser kontrolleres når nogen prøver at oprette en konto, og oprettelsen vil enten blive blokeret, eller der vil blive foretaget en anden handling." @@ -2500,12 +2719,11 @@ da: blocked: "Blokeret?" staged: "Midlertidig?" show_admin_profile: "Admin" - edit_title: "Redigér titel" - save_title: "Gem titel" refresh_browsers: "Gennemtving browser refresh" refresh_browsers_message: "Beskeden er sendt til alle tilsluttede browsere!" show_public_profile: "Vis offentlig profil" impersonate: 'Impersonate' + action_logs: "Aktivitetslog" ip_lookup: "IP opslag" log_out: "Log ud" logged_out: "Bruger er logget ud på alle enheder" @@ -2618,7 +2836,7 @@ da: external_username: "Brugernavn" external_name: "Navn" external_email: "Email" - external_avatar_url: "Profil billed URL" + external_avatar_url: "Profilbillede URL" user_fields: title: "Bruger felter" help: "Tilføj felter, som dine brugere kan udfylde" @@ -2697,6 +2915,7 @@ da: user_preferences: "Brugerpræferencer" tags: "Tags" search: "Søg" + groups: "Grupper" badges: title: Badges new_badge: Nyt Badge @@ -2766,12 +2985,12 @@ da: with_post_time: %{username} for indlæg i %{link} klokken %{time} with_time: %{username} klokken %{time} emoji: - title: "Emotikon" - help: "tilføj en ny emotikon der vil være tilgængelig for alle. (PROTIP: drag & drop flere filer på én gang)" - add: "Tilføj nyt emotikon" + title: "Emoji" + help: "Tilføj en ny emoji, som vil være tilgængelig for alle. (PROTIP: drag & drop flere filer på én gang)" + add: "Tilføj ny emoji" name: "Navn" image: "Billede" - delete_confirm: "Er du sikker på du vil slette emotikonnet: %{name} ?" + delete_confirm: "Er du sikker på du vil slette emojien: %{name} ?" embedding: get_started: "Hvis du vil indlejre Discourse på et andet website, skal du starte med at tilføje dets server." confirm_delete: "Er du sikker på at du vil slette denne server?" @@ -2812,3 +3031,21 @@ da: label: "Ny:" add: "Tilføj" filter: "Søg (URL eller ekstern URL)" + wizard_js: + wizard: + back: "Tilbage" + next: "Næste" + step: "%{current} af %{total}" + upload: "Upload" + uploading: "Uploader..." + quit: "Måske senere" + staff_count: + one: "Dit fællesskab har 1 hjælper" + other: "Dit fællesskab har %{count} hjælpere." + invites: + add_user: "tilføj" + none_added: "Du har ikke inviteret nogen til hjælperteamet. Er du sikker på, at du vil fortsætte?" + roles: + admin: "Admin" + moderator: "Moderator" + regular: "Fast bruger" diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index 769e93c5f8..58dd2818ad 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -246,7 +246,7 @@ de: click_to_show: "Klicke zum Anzeigen." preview: "Vorschau" cancel: "Abbrechen" - save: "Änderungen speichern" + save: "Speichern" saving: "Speichere…" saved: "Gespeichert!" upload: "Hochladen" @@ -281,7 +281,7 @@ de: has_pending_posts: one: "Dieses Thema hat einen Beitrag, der genehmigt werden muss" other: "Dieses Thema hat {{count}} Beiträge, die genehmigt werden müssen" - confirm: "Änderungen speichern" + confirm: "Speichern" delete_prompt: "Möchtest du wirklich %{username} löschen? Damit werden alle Beiträge des Benutzers entfernt und dessen E-Mail- und IP-Adresse geblockt." approval: title: "Beitrag muss genehmigt werden" @@ -366,7 +366,6 @@ de: automatic_group: Automatische Gruppe closed_group: Geschlossene Gruppe is_group_user: "Du bist ein Mitglied in dieser Gruppe." - allow_membership_requests: "Erlaube Benutzern, eine Mitgliedschaftsanfrage an Gruppenbesitzer zu schicken (Erfordert, dass jeder die Gruppe erwähnen kann)" membership: "Mitgliedschaft" name: "Name" user_count: "Anzahl der Mitglieder" @@ -1076,15 +1075,6 @@ de: title: "Hast du vergessen Empfänger hinzuzufügen?" body: "Im Augenblick wird diese Nachricht nur an dich selbst gesendet!" admin_options_title: "Optionale Team-Einstellungen für dieses Thema" - auto_close: - label: "Zeitpunkt der automatischen Schließung:" - error: "Bitte gib einen gültigen Wert ein." - based_on_last_post: "Das Thema erst schließen, wenn der letzte Beitrag mindestens so alt ist." - all: - examples: 'Gib die Anzahl der Stunden (24), eine Uhrzeit (17:30) oder einen Zeitstempel (2013-11-22 14:00) ein.' - limited: - units: "(# Stunden)" - examples: 'Gib die Anzahl der Stunden ein (24).' notifications: title: "Benachrichtigung über @Name-Erwähnungen, Antworten auf deine Beiträge und Themen, Nachrichten, usw." none: "Die Benachrichtigungen können derzeit nicht geladen werden." @@ -1332,11 +1322,7 @@ de: jump_reply_up: zur vorherigen Antwort springen jump_reply_down: zur nachfolgenden Antwort springen deleted: "Das Thema wurde gelöscht" - auto_close_notice: "Dieses Thema wird %{timeLeft} automatisch geschlossen." - auto_close_notice_based_on_last_post: "Dieses Thema wird %{duration} nach der letzten Antwort geschlossen." auto_close_title: 'Automatisches Schließen' - auto_close_save: "Speichern" - auto_close_remove: "Dieses Thema nicht automatisch schließen" auto_close_immediate: one: "Der letzte Beitrag in diesem Thema ist bereits eine Stunde alt. Das Thema wird daher sofort geschlossen." other: "Der letzte Beitrag in diesem Thema ist bereits %{count} Stunden alt. Das Thema wird daher sofort geschlossen." @@ -1369,7 +1355,6 @@ de: '2_8': 'Du wirst Benachrichtigungen erhalten, da du diese Kategorie verfolgst.' '2_4': 'Du wirst Benachrichtigungen erhalten, weil du eine Antwort zu diesem Thema verfasst hast.' '2_2': 'Du wirst Benachrichtigungen erhalten, weil du dieses Thema verfolgst.' - '2': 'Du wirst Benachrichtigungen erhalten, weil du dieses Thema gelesen hast.' '1_2': 'Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet.' '1': 'Du wirst benachrichtigt, wenn jemand deinen @Namen erwähnt oder auf deinen Beitrag antwortet.' '0_7': 'Du ignorierst alle Benachrichtigungen dieser Kategorie.' @@ -1405,7 +1390,6 @@ de: open: "Thema öffnen" close: "Thema schließen" multi_select: "Beiträge auswählen..." - auto_close: "Automatisch schließen…" pin: "Thema anheften..." unpin: "Thema loslösen..." unarchive: "Thema aus Archiv holen" @@ -2518,32 +2502,12 @@ de: customize: title: "Anpassen" long_title: "Anpassungen" - css: "CSS" - header: "Kopfbereich" - top: "Anfang" - footer: "Fußzeile" - embedded_css: "Eingebettetes CSS" - head_tag: - text: "" - title: "HTML das vor dem Tag eingefügt wird." - body_tag: - text: "" - title: "HTML das vor dem Tag eingefügt wird." - override_default: "Das Standard-Stylesheet nicht verwenden" - enabled: "Aktiviert?" preview: "Vorschau" - undo_preview: "Vorschau entfernen" - rescue_preview: "Standard-Style" - explain_preview: "Zeige die Website mit benutzerdefiniertem Stylesheet an" - explain_undo_preview: "Gehe zurück zum aktuell aktivierten, benutzerdefinierten Stylesheet" - explain_rescue_preview: "Zeige die Website mit dem Standard-Stylesheet an" save: "Speichern" new: "Neu" new_style: "Neuer Style" import: "Importieren" - import_title: "Datei auswählen oder Text einfügen" delete: "Löschen" - delete_confirm: "Diese Anpassung löschen?" about: "Ändere die Stylesheets (CSS) und den HTML-Header auf der Website. Füge eine Anpassung hinzu, um zu starten." color: "Farbe" opacity: "Transparenz" @@ -2556,13 +2520,9 @@ de: none_selected: "Wähle eine E-Mail-Vorlage aus, um diese zu bearbeiten." revert: "Änderungen verwerfen" revert_confirm: "Möchtest du wirklich deine Änderungen verwerfen?" - css_html: - title: "CSS/HTML" - long_title: "CSS und HTML Anpassungen" colors: title: "Farben" long_title: "Farbschemata" - about: "Farbschemen erlauben dir die auf der Seite benutzen Farben zu ändern ohne CSS schreiben zu müssen. Füge ein Schema hinzu, um zu beginnen." new_name: "Neues Farbschema" copy_name_prefix: "Kopie von" delete_confirm: "Dieses Farbschema löschen?" @@ -2701,8 +2661,6 @@ de: change_trust_level: "Vertrauensstufe ändern" change_username: "Benutzernamen ändern" change_site_setting: "Einstellungen ändern" - change_site_customization: "Anpassungen ändern" - delete_site_customization: "Anpassungen löschen" change_site_text: "Text ändern" suspend_user: "Benutzer sperren" unsuspend_user: "Benutzer entsperren" @@ -2837,8 +2795,6 @@ de: blocked: "Geblockt?" staged: "Vorbereitet?" show_admin_profile: "Administration" - edit_title: "Titel bearbeiten" - save_title: "Titel speichern" refresh_browsers: "Aktualisierung im Browser erzwingen" refresh_browsers_message: "Nachricht wurde an alle Clients gesendet!" show_public_profile: "Zeige öffentliches Profil" diff --git a/config/locales/client.el.yml b/config/locales/client.el.yml index 87298adb17..c337115e03 100644 --- a/config/locales/client.el.yml +++ b/config/locales/client.el.yml @@ -366,7 +366,6 @@ el: automatic_group: Αυτόματη ομάδα closed_group: Κλειστή ομάδα is_group_user: "Είσαι μέλος αυτής της ομάδας" - allow_membership_requests: "Επίτρεψε στους χρήστες να στέλνουν αίτημα στους ιδιοκτήτες των ομάδων για να γίνουν μέλη (Απαιτείται όλοι να είναι ικανοί να αναφέρουν την ομάδα)" membership: "Ιδιότητα μέλους" name: "Όνομα" user_count: "Αριθμός μελών" @@ -1076,15 +1075,6 @@ el: title: "Ξεχάσατε να προσθέσετε αποδέκτες;" body: "Αυτή τη στιγμή το ηλεκτρονικό μήνυμα στέλνεται μόνο σε εσάς!" admin_options_title: "Προαιρετικές ρυθμίσεις προσωπικού για αυτό το θέμα" - auto_close: - label: "Χρόνος Αυτόματου Κλεισίματος νήματος:" - error: "Παρακαλώ δώσε μια έγκυρη τιμή." - based_on_last_post: "Η τελευταία ανάρτηση στο νήμα να κλείσει μόνο εφόσον είναι τόσο παλιά." - all: - examples: 'Δώσε τον αριθμό των ωρών (24), την ακριβή ώρα (17:30) ή μια χρονοσήμανση (2013-11-22 14:00).' - limited: - units: "(# ωρών)" - examples: 'Δώσε το πλήθος των ωρών (24).' notifications: title: "ειδοποιήσεις για αναφορές στο @name, απαντήσεις στις αναρτήσεις σου και στα νήματά σου, προσωπικά μηνύματα, κλπ." none: "Αυτή τη στιγμή δεν είναι δυνατόν να φορτωθούν οι ειδοποιήσεις." @@ -1332,11 +1322,7 @@ el: jump_reply_up: μετάβαση στην απάντηση που προηγείται jump_reply_down: μετάβαση στην απάντηση που ακολουθεί deleted: "Το νήμα έχει διαγραφεί " - auto_close_notice: "Το νήμα αυτό θα κλείσει αυτόματα σε %{timeLeft}." - auto_close_notice_based_on_last_post: "Το νήμα αυτό θα κλείσει %{duration} μετά την τελευταία απάντηση." auto_close_title: 'Ρυθμίσεις για το αυτόματο κλείσιμο' - auto_close_save: "Αποθήκευση" - auto_close_remove: "Αυτό το νήμα να μην κλείσει αυτόματα " auto_close_immediate: one: "Η τελευταία δημοσίευση στο νήμα είναι ήδη 1 ώρα παλιό, έτσι το νήμα θα κλείσει αμέσως." other: "Η τελευταία δημοσίευση είναι ήδη %{count} ώρες παλιό, έτσι το νήμα θα κλείσει αμέσως." @@ -1369,7 +1355,6 @@ el: '2_8': 'Θα λαμβάνεις ειδοποιήσεις επειδή παρακολουθείς αυτή την κατηγορία.' '2_4': 'Θα λαμβάνεις ειδοποιήσεις επειδή ανάρτησες μια απάντηση σε αυτό το νήμα.' '2_2': 'Θα λαμβάνεις ειδοποιήσεις επειδή παρακολουθείς αυτό το νήμα.' - '2': 'Θα λαμβάνεις ειδοποιήσεις επειδή διάβασες αυτό το νήμα.' '1_2': 'Θα λαμβάνεις ειδοποιήσεις εάν κάποιος αναφέρει το @όνομά σου ή απαντήσει σε εσένα.' '1': 'Θα λαμβάνεις ειδοποιήσεις, εάν κάποιος αναφέρει το @όνομά σου ή απαντήσει σε εσένα.' '0_7': 'Θα αγνοούνται όλες οι ειδοποιήσεις από αυτή την κατηγορία.' @@ -1405,7 +1390,6 @@ el: open: "Νέο νήμα" close: "Κλείσε το νήμα" multi_select: "Διάλεξε αναρτήσεις..." - auto_close: "Αυτόματο κλείσιμο..." pin: "Καρφίτσωσε το νήμα..." unpin: "Ξεκαρφίτσωσε το νήμα..." unarchive: "Επανέφερε το νήμα από το αρχείο" @@ -2522,32 +2506,12 @@ el: customize: title: "Προσαρμογή" long_title: "Προσαρμογές ιστότοπου" - css: "CSS" - header: "Κεφαλίδα" - top: "Κορυφή" - footer: "Υποσέλιδο" - embedded_css: "Ενσωματωμένο CSS" - head_tag: - text: "" - title: "HTML που θα μπει πριν από την ετικέτα " - body_tag: - text: "" - title: "HTML που θα μπει πριν από την ετικέτα " - override_default: "Να μη χρησιμοποιηθεί το προκαθορισμένο αρχείο στυλ" - enabled: "Ενεργοποιημένο;" preview: "προεπισκόπηση " - undo_preview: "αφαίρεση προεπισκόπησης" - rescue_preview: "Προκαθορισμένο στυλ" - explain_preview: "Δείξε τον ιστότοπο με αυτό το προσαρμοσμένο αρχείο στυλ" - explain_undo_preview: "Επιστροφή στο τρέχον προσαρμοσμένο αρχείο στυλ" - explain_rescue_preview: "Δείξε τον ιστότοπο με το προκαθορισμένο αρχείο στυλ" save: "Αποθήκευση" new: "Νέο" new_style: "Νέο στυλ" import: "Εισαγωγή" - import_title: "Επίλεξε ένα φάκελο ή κάνε επικόλληση κείμενο" delete: "Σβήσιμο" - delete_confirm: "Να σβηστεί αυτή η προσαρμογή;" about: "Προσάρμοσε τα αρχεία στυλ CSS και την κεφαλίδα HTML του ιστότοπου. Πρόσθεσε μια προσαρμογή για να ξεκινήσεις." color: "Χρώμα" opacity: "Αδιαφάνεια" @@ -2560,13 +2524,9 @@ el: none_selected: "Επίλεξε ένα πρότυπο ηλεκτρονικού μηνύματος να ξεκινήσεις την επεξεργασία." revert: "Επαναφορά αλλαγών" revert_confirm: "Είσαι βέβαιος πως θέλεις να επαναφέρεις τις αλλαγές;" - css_html: - title: "CSS/HTML" - long_title: "Προσαρμογές CSS και HTML" colors: title: "Χρώματα" long_title: "Χρωματικά σύνολα" - about: "Προσάρμοσε τα χρώματα του ιστότοπου χωρίς να γράψεις CSS. Πρόσθεσε ένα χρωματικό σύνολο για να ξεκινήσεις." new_name: "Νέο χρωματικό σύνολο" copy_name_prefix: "Αντίγραφο του" delete_confirm: "Να διαγραφεί αυτό το χρωματικό σύνολο;" @@ -2705,8 +2665,6 @@ el: change_trust_level: "αλλαγή επιπέδου εμπιστοσύνης" change_username: "αλλαγή χρηστώνυμου" change_site_setting: "αλλαγή ρυθμίσεων ιστότοπου" - change_site_customization: "αλλαγή των προσαρμογών του ιστότοπου" - delete_site_customization: "σβήσιμο των προσαρμογών του ιστότοπου" change_site_text: "αλλαγή θέσης κειμένου" suspend_user: "Αποκλεισμός του χρήστη" unsuspend_user: "Αναίρεση αποκλεισμού χρήστη" @@ -2841,8 +2799,6 @@ el: blocked: "Αποκλείστηκε; " staged: "Σταδιακή?" show_admin_profile: "Διαχείριση" - edit_title: "Επεξεργασία τίτλου" - save_title: "Αποθήκευση τίτλου" refresh_browsers: "Να ανανεωθεί αναγκαστικά η σελίδα" refresh_browsers_message: "Το μήνυμα στάλθηκε σε όλους τους πελάτες!" show_public_profile: "Εμφάνισε το Δημόσιο Προφίλ" diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index caa2c4e0c5..a778d441ae 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -127,6 +127,7 @@ en: other: "%{count} years later" previous_month: 'Previous Month' next_month: 'Next Month' + placeholder: Pick a date share: topic: 'share a link to this topic' post: 'post #%{postNumber}' @@ -174,6 +175,9 @@ en: bootstrap_mode_enabled: "To make launching your new site easier, you are in bootstrap mode. All new users will be granted trust level 1 and have daily email digest updates enabled. This will be automatically turned off when total user count exceeds %{min_users} users." bootstrap_mode_disabled: "Bootstrap mode will be disabled in next 24 hours." + themes: + default_description: "Default" + s3: regions: us_east_1: "US East (N. Virginia)" @@ -181,6 +185,7 @@ en: us_west_2: "US West (Oregon)" us_gov_west_1: "AWS GovCloud (US)" eu_west_1: "EU (Ireland)" + eu_west_2: "EU (London)" eu_central_1: "EU (Frankfurt)" ap_southeast_1: "Asia Pacific (Singapore)" ap_southeast_2: "Asia Pacific (Sydney)" @@ -441,6 +446,7 @@ en: posts: "Posts" mentions: "Mentions" messages: "Messages" + notification_level: "Default notification level for group messages" alias_levels: title: "Who can message and @mention this group?" nobody: "Nobody" @@ -633,6 +639,7 @@ en: revoke_access: "Revoke Access" undo_revoke_access: "Undo Revoke Access" api_approved: "Approved:" + theme: "Theme" staff_counters: flags_given: "helpful flags" @@ -1287,6 +1294,7 @@ en: no_more_results: "No more results found." searching: "Searching ..." post_format: "#{{post_number}} by {{username}}" + results_page: "Search Results" context: user: "Search posts by @{{username}}" @@ -1479,16 +1487,19 @@ en: topic_status_update: title: "Set Topic Timer" save: "Set Timer" - time: "Time:" + num_of_hours: "Number of hours:" remove: "Remove Timer" publish_to: "Publish To:" + when: "When:" auto_update_input: - limited: - units: "(# of hours)" - examples: 'Enter number of hours (24).' - all: - units: "" - examples: 'Enter number of hours (24), absolute time (17:30) or timestamp (2013-11-22 14:00).' + none: "" + later_today: "Later today" + tomorrow: "Tomorrow" + later_this_week: "Later this week" + this_weekend: "This weekend" + next_week: "Next week" + pick_date_and_time: "Pick date and time" + set_based_on_last_post: "Close based on last post" publish_to_category: title: "Schedule Publishing" temp_open: @@ -1499,7 +1510,7 @@ en: title: "Close Temporarily" auto_close: title: "Auto-Close Topic" - label: "Auto-close topic time:" + label: "Auto-close topic hours:" error: "Please enter a valid value." based_on_last_post: "Don't close until the last post in the topic is at least this old." @@ -1541,10 +1552,10 @@ en: "3_2": 'You will receive notifications because you are watching this topic.' "3_1": 'You will receive notifications because you created this topic.' "3": 'You will receive notifications because you are watching this topic.' - "2_8": 'You will receive notifications because you are tracking this category.' - "2_4": 'You will receive notifications because you posted a reply to this topic.' - "2_2": 'You will receive notifications because you are tracking this topic.' - "2": 'You will receive notifications because you read this topic.' + "2_8": 'You will see a count of new replies because you are tracking this category.' + "2_4": 'You will see a count of new replies because you posted a reply to this topic.' + "2_2": 'You will see a count of new replies because you are tracking this topic.' + "2": 'You will see a count of new replies because you read this topic.' "1_2": 'You will be notified if someone mentions your @name or replies to you.' "1": 'You will be notified if someone mentions your @name or replies to you.' "0_7": 'You are ignoring all notifications in this category.' @@ -1721,7 +1732,7 @@ en: instructions_warn: "Note that any notifications about this post will not be transferred to the new user retroactively.
    Warning: Currently, no post-dependent data is transferred over to the new user. Use with caution." change_timestamp: - title: "Change Timestamp" + title: "Change Timestamp..." action: "change timestamp" invalid_timestamp: "Timestamp cannot be in the future." error: "There was an error changing the timestamp of the topic." @@ -2005,8 +2016,6 @@ en: security: "Security" special_warning: "Warning: This category is a pre-seeded category and the security settings cannot be edited. If you do not wish to use this category, delete it instead of repurposing it." images: "Images" - auto_close_label: "Auto-close topics after:" - auto_close_units: "hours" email_in: "Custom incoming email address:" email_in_allow_strangers: "Accept emails from anonymous users with no accounts" email_in_disabled: "Posting new topics via email is disabled in the Site Settings. To enable posting new topics via email, " @@ -2592,6 +2601,7 @@ en: add_members: "Add members" custom: "Custom" bulk_complete: "The users have been added to the group." + bulk_complete_users_not_added: "These users were not added:" bulk: "Bulk Add to Group" bulk_paste: "Paste a list of usernames or emails, one per line:" bulk_select: "(select a group)" @@ -2779,36 +2789,22 @@ en: customize: title: "Customize" long_title: "Site Customizations" - css: "CSS" - header: "Header" - top: "Top" - footer: "Footer" - embedded_css: "Embedded CSS" - head_tag: - text: "" - title: "HTML that will be inserted before the tag" - body_tag: - text: "" - title: "HTML that will be inserted before the tag" - override_default: "Do not include standard style sheet" - enabled: "Enabled?" preview: "preview" - undo_preview: "remove preview" - rescue_preview: "default style" - explain_preview: "See the site with this custom stylesheet" - explain_undo_preview: "Go back to the currently enabled custom stylesheet" - explain_rescue_preview: "See the site with the default stylesheet" + explain_preview: "See the site with this theme enabled" save: "Save" new: "New" new_style: "New Style" import: "Import" - import_title: "Select a file or paste text" delete: "Delete" - delete_confirm: "Delete this customization?" + delete_confirm: "Delete this theme?" about: "Modify CSS stylesheets and HTML headers on the site. Add a customization to start." color: "Color" opacity: "Opacity" copy: "Copy" + copy_to_clipboard: "Copy to Clipboard" + copied_to_clipboard: "Copied to Clipboard" + copy_to_clipboard_error: "Error copying data to Clipboard" + theme_owner: "Not editable, owned by:" email_templates: title: "Email Templates" subject: "Subject" @@ -2818,13 +2814,69 @@ en: revert: "Revert Changes" revert_confirm: "Are you sure you want to revert your changes?" - css_html: - title: "CSS/HTML" - long_title: "CSS and HTML Customizations" + theme: + import_theme: "Import Theme" + customize_desc: "Customize:" + title: "Themes" + long_title: "Amend colors, CSS and HTML contents of your site" + edit: "Edit" + edit_confirm: "This is a remote theme, if you edit CSS/HTML your changes will be erased next time you update the theme." + common: "Common" + desktop: "Desktop" + mobile: "Mobile" + preview: "Preview" + is_default: "Theme is enabled by default" + user_selectable: "Theme can be selected by users" + color_scheme: "Color Scheme" + color_scheme_select: "Select colors to be used by theme" + custom_sections: "Custom sections:" + theme_components: "Theme Components" + child_themes_check: "Theme includes other child themes" + css_html: "Custom CSS/HTML" + edit_css_html: "Edit CSS/HTML" + edit_css_html_help: "You have not edited any CSS or HTML" + import_web_tip: "Repository containing theme" + import_file_tip: ".dcstyle.json file containing theme" + about_theme: "About Theme" + license: "License" + component_of: "Theme is a component of:" + update_to_latest: "Update to Latest" + check_for_updates: "Check for Updates" + updating: "Updating..." + up_to_date: "Theme is up-to-date, last checked:" + add: "Add" + commits_behind: + one: "Theme is 1 commit behind!" + other: "Theme is {{count}} commit behind!" + scss: + text: "CSS" + title: "Enter custom CSS, we accept all valid CSS and SCSS styles" + header: + text: "Header" + title: "Enter HTML to display above site header" + after_header: + text: "After Header" + title: "Enter HTML to display on all pages after header" + footer: + text: "Footer" + title: "Enter HTML to display on page footer" + embedded_scss: + text: "Embedded CSS" + title: "Enter custom CSS to deliver with embedded version of comments" + head_tag: + text: "" + title: "HTML that will be inserted before the tag" + body_tag: + text: "" + title: "HTML that will be inserted before the tag" colors: + select_base: + title: "Select base color scheme" + description: "Base scheme:" title: "Colors" + edit: "Edit Color Schemes" long_title: "Color Schemes" - about: "Modify the colors used on the site without writing CSS. Add a scheme to start." + about: "Modify the colors used by your themes. Create a new color scheme to start." new_name: "New Color Scheme" copy_name_prefix: "Copy of" delete_confirm: "Delete this color scheme?" @@ -2965,8 +3017,8 @@ en: change_trust_level: "change trust level" change_username: "change username" change_site_setting: "change site setting" - change_site_customization: "change site customization" - delete_site_customization: "delete site customization" + change_theme: "change theme" + delete_theme: "delete theme" change_site_text: "change site text" suspend_user: "suspend user" unsuspend_user: "unsuspend user" diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index 1365b90b0e..24ed89f13e 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -140,6 +140,8 @@ es: emails_are_disabled: "Todos los emails salientes han sido desactivados por un administrador. No se enviará ninguna notificación por email." bootstrap_mode_enabled: "Para lanzar tu nuevo sitio más fácilmente, estás en modo de arranque. A todos los nuevos usuarios se les concederá el nivel 1 de confianza y recibirán resúmenes diarios por email. Esto se desactivará automáticamente cuando el número total de usuarios exceda los %{min_users}." bootstrap_mode_disabled: "El modo de arranque se desactivará en las próximas 24 horas." + themes: + default_description: "Por defecto" s3: regions: us_east_1: "EEUU Este (Virginia del Norte)" @@ -147,6 +149,7 @@ es: us_west_2: "EEUU Oeste (Oregon)" us_gov_west_1: "AWS GovCloud (US)" eu_west_1: "UE (Irlanda)" + eu_west_2: "EU (Londres)" eu_central_1: "UE (Frankfurt)" ap_southeast_1: "Asia Pacific (Singapur)" ap_southeast_2: "Asia Pacific (Sydney)" @@ -363,7 +366,6 @@ es: automatic_group: Grupo automático closed_group: Grupo cerrado is_group_user: "Eres miembro de este grupo" - allow_membership_requests: "Permitir a los usuarios solicitudes para ser miembro a los dueños del grupo (es necesario que todo el mundo pueda mencionar al grupo)" membership: "Membresía" name: "Nombre" user_count: "Número de miembros" @@ -566,6 +568,7 @@ es: revoke_access: "Revocar acceso" undo_revoke_access: "Deshacer revocación de acceso" api_approved: "Aprobado:" + theme: "Tema" staff_counters: flags_given: "reportes útiles" flagged_posts: "posts reportados" @@ -935,6 +938,10 @@ es: not_allowed_from_ip_address: "No puedes iniciar sesión desde esa dirección IP." admin_not_allowed_from_ip_address: "No puedes iniciar sesión como admin desde esta dirección IP." resend_activation_email: "Has clic aquí para enviar el email de activación nuevamente." + resend_title: "Enviar de nuevo el email de activación" + change_email: "Cambiar dirección de email" + provide_new_email: "Escribe una nueva dirección y le reenviaremos el correo de confirmación." + submit_new_email: "Actualizar dirección de email" sent_activation_email_again: "Te hemos enviado otro e-mail de activación a {{currentemail}}. Podría tardar algunos minutos en llegar; asegúrate de revisar tu carpeta de spam." to_continue: "Por favor, inicia sesión" preferences: "Debes tener una sesión iniciada para cambiar tus preferencias de usuario." @@ -1073,15 +1080,6 @@ es: title: "¿Olvidaste añadir destinatarios?" body: "¡Vas a enviarte este mensaje a ti mismo!" admin_options_title: "Opciones de moderación para este tema" - auto_close: - label: "Tiempo para cierre automático del tema" - error: "Por favor introduzca un valor válido." - based_on_last_post: "No cerrar hasta que el último post en el tema es al menos así de antiguo." - all: - examples: 'Introduzca el número de horas (24), tiempo absoluto (17:30) o timestamp (2013-11-22 14:00).' - limited: - units: "(# de horas)" - examples: 'Introduzca el número de horas (24).' notifications: title: "notificaciones por menciones a tu @nombre, respuestas a tus posts y temas, mensajes, etc" none: "No se han podido cargar las notificaciones." @@ -1151,6 +1149,7 @@ es: sort_by: "Ordenar por" relevance: "Relevancia" latest_post: "Post más reciente" + latest_topic: "Último tema" most_viewed: "Más visto" most_liked: "Más \"Me gusta\" recibidos" select_all: "Seleccionar todo" @@ -1329,11 +1328,33 @@ es: jump_reply_up: saltar a la primera respuesta jump_reply_down: saltar a la última respuesta deleted: "El tema ha sido borrado" - auto_close_notice: "Este tema se cerrará automáticamente en %{timeLeft}." - auto_close_notice_based_on_last_post: "Este tema cerrara %{duration} después de la última respuesta." + topic_status_update: + time: "Tiempo:" + publish_to: "Publicar a:" + auto_update_input: + limited: + units: "(número de horas)" + examples: 'Introduce las horas (24).' + all: + examples: 'Introduce el número de horas (24), hora absoluta (17:30) o timestamp (2013-11-22 14:00).' + publish_to_category: + title: "Programar publicación" + temp_open: + title: "Abrir temporalmente" + auto_reopen: + title: "Abrir tema automáticamente" + temp_close: + title: "Cerrar temporalmente" + auto_close: + title: "Cerrar tema automáticamente" + error: "Por favor, introduce un valor válido." + based_on_last_post: "No cerrar hasta que el último post en el tema sea al menos así de antiguo." + status_update_notice: + auto_open: "Este tema se abrirá automáticamente %{timeLeft}." + auto_close: "Este tema se cerrará automáticamente %{timeLeft}." + auto_publish_to_category: "Este tema será publicado en #%{categoryName} %{timeLeft}." + auto_close_based_on_last_post: "Este tema se cerrará %{duration} después de la última respuesta." auto_close_title: 'Configuración de auto-cerrado' - auto_close_save: "Guardar" - auto_close_remove: "No Auto-Cerrar Este Tema" auto_close_immediate: one: "El último post se publicó hace 1 hora, por lo que el tema se cerrará inmediatamente." other: "El último post se publicó hace %{count} horas, por lo que el tema se cerrará inmediatamente." @@ -1366,7 +1387,7 @@ es: '2_8': 'Recibirás notificaciones porque estás siguiendo esta categoría.' '2_4': 'Recibirás notificaciones porque has publicado una respuesta en este tema.' '2_2': 'Recibirás notificaciones porque estás siguiendo este tema.' - '2': 'Recibirás notificaciones porque has leído este tema.' + '2': 'Recibirás notificaciones porque has leído este tema.' '1_2': 'Se te notificará solo si alguien menciona tu @nombre o te responde a un post.' '1': 'Se te notificará si alguien menciona tu @nombre o te responde a un post.' '0_7': 'Estás ignorando todas las notificaciones en esta categoría.' @@ -1402,7 +1423,6 @@ es: open: "Abrir tema" close: "Cerrar tema" multi_select: "Seleccionar posts..." - auto_close: "Auto-cierre..." pin: "Destacar tema..." unpin: "Dejar de destacar..." unarchive: "Desarchivar Tema" @@ -1800,6 +1820,7 @@ es: num_featured_topics: "Número de temas a mostrar en la página de categorías:" subcategory_num_featured_topics: "Número de temas destacados a mostrar en la página superior de categorías:" all_topics_wiki: "Hacer todos los temas wiki por defecto" + subcategory_list_style: "Estilo de la lista de subcategorías:" allow_badges_label: "Permitir conceder distintivos en esta categoría" edit_permissions: "Editar permisos" add_permission: "Añadir permisos" @@ -1837,6 +1858,11 @@ es: created: "Cread" sort_ascending: 'Ascendente' sort_descending: 'Descendente' + subcategory_list_styles: + rows: "Filas" + rows_with_featured_topics: "Filas con temas destacados" + boxes: "Caja" + boxes_with_featured_topics: "Cajas con temas destacados" flagging: title: '¡Gracias por ayudar a mantener una comunidad civilizada!' action: 'Reportar post' @@ -2510,32 +2536,12 @@ es: customize: title: "Personalizar" long_title: "Personalizaciones del sitio" - css: "CSS" - header: "Encabezado" - top: "Top" - footer: "Pie de página" - embedded_css: "CSS embebido" - head_tag: - text: "" - title: "HTML insertado antes de la etiqueta " - body_tag: - text: "" - title: "HTML insertado antes de la etiqueta " - override_default: "No incluir hoja de estilo estándar" - enabled: "¿Activado?" preview: "vista previa" - undo_preview: "eliminar vista previa" - rescue_preview: "estilo por defecto" - explain_preview: "Ver el sitio con esta hoja de estilo" - explain_undo_preview: "Volver a la hoja de estilo personalizada activada actualmente" - explain_rescue_preview: "Ver el sitio con la hoja de estilo por defecto" save: "Guardar" new: "Nuevo" new_style: "Nuevo Estilo" import: "Importar" - import_title: "Selecciona un archivo o pega texto" delete: "Eliminar" - delete_confirm: "¿Eliminar esta personalización?" about: "Modifica hojas de estilo CSS y cabeceras HTML en el sitio. Añade una personalización para empezar." color: "Color" opacity: "Opacidad" @@ -2548,13 +2554,40 @@ es: none_selected: "Selecciona un 'diseño de email' para comenzar a editar" revert: "Revertir los cambios" revert_confirm: "¿Estás seguro de querer revertir los cambios?" - css_html: - title: "CSS/HTML" - long_title: "Personalizaciones CSS y HTML" + theme: + edit: "Editar" + common: "Común" + desktop: "Ordenador" + mobile: "Móvil" + preview: "Previsualizar" + is_default: "Tema activado por defecto" + user_selectable: "Tema seleccionable por los usuarios" + color_scheme: "Paleta de color" + color_scheme_select: "Colores a utilizar por el tema:" + included_themes: "Temas incluidos" + child_themes_check: "El tema incluye otros temas" + css_html: "CSS/HTML personalizado" + edit_css_html: "Editar CSS/HTML" + edit_css_html_help: "No se ha editado ningún CSS ni HTML" + import_web_tip: "Repositorio que contiene el tema" + about_theme: "Sobre el tem" + license: "Licencia" + update_to_latest: "Actualizar a la última versión" + check_for_updates: "Comprobar actualizaciones" + updating: "Actualizando..." + up_to_date: "El tema está actualizado, última comprobación:" + add: "Añadir" + commits_behind: + one: "¡El tema está 1 commit atrasado!" + other: "¡El tema está {{count}} commits atrasado!" + scss: + text: "CSS" + title: "Introducir CSS personalizado, se acepta tanto CSS como SCSS" + header: + text: "Header" colors: title: "Colores" long_title: "Esquemas de color" - about: "Modifica los colores utilizados en el sitio sin editar el CSS. Añade un esquema de color para empezar." new_name: "Nuevo esquema de color" copy_name_prefix: "Copia de" delete_confirm: "¿Eliminar este esquema de color?" @@ -2693,8 +2726,8 @@ es: change_trust_level: "cambiar nivel de confianza" change_username: "cambiar nombre de usuario" change_site_setting: "cambiar configuración del sitio" - change_site_customization: "cambiar customización del sitio" - delete_site_customization: "borrar customización del sitio" + change_theme: "cambiar tem" + delete_theme: "borrar tema" change_site_text: "cambiar textos" suspend_user: "suspender usuario" unsuspend_user: "desbloquear usuario" @@ -2829,8 +2862,6 @@ es: blocked: "¿Bloqueado?" staged: "¿Provisional?" show_admin_profile: "Administrador" - edit_title: "Editar título" - save_title: "Guardar título" refresh_browsers: "Forzar recarga del navegador" refresh_browsers_message: "¡Mensaje enviado a todos los clientes!" show_public_profile: "Ver perfil público" @@ -2841,6 +2872,7 @@ es: logged_out: "El usuario ha cerrado sesión desde todos los dispositivos" revoke_admin: 'Revocar administrador' grant_admin: 'Conceder administración' + grant_admin_confirm: "Te hemos enviado un email para verificar el nuevo administrador. Por favor, ábrelo y sigue las instrucciones." revoke_moderation: 'Revocar moderación' grant_moderation: 'Conceder moderación' unblock: 'Desbloquear' diff --git a/config/locales/client.et.yml b/config/locales/client.et.yml index 06ec14de1f..6c7ccc40ae 100644 --- a/config/locales/client.et.yml +++ b/config/locales/client.et.yml @@ -362,7 +362,6 @@ et: automatic_group: Automaatne grupp closed_group: Suletud grupp is_group_user: "Oled selle grupi liige" - allow_membership_requests: "Luba kasutajatel saata grupi omanikele liikmelisuse taotlusi (eeldab, et igaüks saab gruppi mainida)" membership: "Liikmelisus" name: "Nimi" user_count: "Liikmete arv" @@ -1071,15 +1070,6 @@ et: title: "Kas unustasid saajad lisada?" body: "Hetkel saadetakse see sõnum vaid sulle endale!" admin_options_title: "Meeskonna valikulised sätted selle teema jaoks" - auto_close: - label: "Teema automaatse sulgemise aeg:" - error: "Palun sisesta lubatav väärtus." - based_on_last_post: "Ära sule enne, kui selle teema viimane postitus on vähemalt nii vana." - all: - examples: 'Sisesta tundide arv (24), absoluutne aeg (17:30) või ajatempel (2013-11-22 14:00).' - limited: - units: "(tundide arv)" - examples: 'Sisesta tundide arv (24).' notifications: title: "teavitused @name mainimiste, oma postitustele ja teemadele vastamiste, sõnumite, jne kohta" none: "Hetkel ei saa teavitusi laadida." @@ -1325,11 +1315,7 @@ et: jump_reply_up: hüppa varasema vastuse juurde jump_reply_down: hüppa hilisema vastuse juurde deleted: "See teema on kustutatud" - auto_close_notice: "See teema sulgub %{timeLeft} pärast ise." - auto_close_notice_based_on_last_post: "See teema sulgub %{duration} peale viimast vastust." auto_close_title: 'Automaatse sulgumise sätted' - auto_close_save: "Salvesta" - auto_close_remove: "Ära lase sel teemal automaatselt sulguda" auto_close_immediate: one: "Viimane postitus selles teemas on juba 1 tund aega vana, seega sulgub see teema nüüd." other: "Viimane postitus selles teemas on juba %{hours} tundi vana, seega sulgub see teema nüüd." @@ -1362,7 +1348,6 @@ et: '2_8': 'Sulle saabuvad teavitused, kuna jälgid seda foorumit.' '2_4': 'Sulle saabuvad teavitused kuna postitasid siia teemasse vastuse.' '2_2': 'Sulle saabuvad teavitused kuna jälgid seda teemat.' - '2': 'Sulle saabuvad teavitused kuna luges seda teemat.' '1_2': 'Sind teavitatakse, kui keegi Teie @name mainib või Teile vastab.' '1': 'Sind teavitatakse, kui keegi Sinu @name mainib või Sulle vastab.' '0_7': 'Eirad kõiki teavitusi selles foorumis.' @@ -1398,7 +1383,6 @@ et: open: "Ava teema" close: "Sulge teema" multi_select: "Vali postitused..." - auto_close: "Sulge automaatselt..." pin: "Tõsta teema esile..." unpin: "Eemalda teema esiletõstmine..." unarchive: "Taasta teema arhiivist" @@ -2490,32 +2474,12 @@ et: customize: title: "Kohanda" long_title: "Saidi kohandused" - css: "CSS" - header: "Päis" - top: "Ülemine" - footer: "Jalus" - embedded_css: "Sängitatud CSS" - head_tag: - text: "" - title: "HTML, mida lisatakse enne silti" - body_tag: - text: "" - title: "HTML, mida lisatakse enne silti" - override_default: "Ära standardset laadilehte kaasa" - enabled: "Sisse lülitatud?" preview: "eelvaade" - undo_preview: "eemalda eelvaade" - rescue_preview: "vaikelaad" - explain_preview: "Vaata saiti selle kohandatud laadilehega" - explain_undo_preview: "Pöördu tagasi hetkel aktiivse kohandatud laadilehe juurde" - explain_rescue_preview: "Vaata saiti vaikimisi laadilehega" save: "Salvesta" new: "Uus" new_style: "Uus stiil" import: "Impordi" - import_title: "Vali fail või kleebi tekst" delete: "Kustuta" - delete_confirm: "Kustutan selle kohanduse?" about: "Muuda saidi CSS laadilehed ja HTML päised. Alustamiseks lisa kohandus." color: "Värv" opacity: "Läbipaistvus" @@ -2528,13 +2492,9 @@ et: none_selected: "Redigeerimise alustamiseks vali meili mall." revert: "Loobu muudatustest" revert_confirm: "Oled kindel, et soovid oma muudatustest loobuda?" - css_html: - title: "CSS/HTML" - long_title: "CSS and HTML modifikatsioonid" colors: title: "Värvid" long_title: "Värvistikud" - about: "Muuda saidil kasutatavaid värve ilma CSS-i kirjutamata. Alustamiseks lisa uus värvistik." new_name: "Uus värvistik" copy_name_prefix: "Koopia sellest" delete_confirm: "Kustutan selle värvistiku?" @@ -2673,8 +2633,6 @@ et: change_trust_level: "muuda usaldustaset" change_username: "muuda kasutajanime" change_site_setting: "muuda saidi sätet" - change_site_customization: "muuda saidi kohandusi" - delete_site_customization: "kustuta saidi kohandused" change_site_text: "muuda saidi teksti" suspend_user: "peata kasutaja" unsuspend_user: "taasluba kasutaja" @@ -2809,8 +2767,6 @@ et: blocked: "Blokeeritud?" staged: "Ettevalmistamisel?" show_admin_profile: "Admin" - edit_title: "Muuda tiitlit" - save_title: "Salvesta tiitel" refresh_browsers: "Sunni brauserit värskendama" refresh_browsers_message: "Sõnum saadetud kõigile klientidele!" show_public_profile: "Näita avalikku profiili" diff --git a/config/locales/client.fa_IR.yml b/config/locales/client.fa_IR.yml index 3c70c5e816..e66f785ae1 100644 --- a/config/locales/client.fa_IR.yml +++ b/config/locales/client.fa_IR.yml @@ -10,20 +10,20 @@ fa_IR: number: format: separator: "." - delimiter: "," + delimiter: "،" human: storage_units: format: '%n %u' units: byte: other: بایت - gb: GB - kb: KB - mb: MB - tb: TB + gb: گیگابایت + kb: کیلوبایت + mb: مگابایت + tb: ترابایت short: - thousands: "{{number}}k" - millions: "{{number}}M" + thousands: "{{number}} هزار" + millions: "{{number}} میلیون" dates: time: "h:mm a" timeline_date: "MMM YYYY" @@ -81,8 +81,9 @@ fa_IR: other: "%{count} ماه بعد" x_years: other: "%{count} سال بعد" - previous_month: 'ماه پیشین' + previous_month: 'ماه قبل' next_month: 'ماه بعد' + placeholder: انتخاب تاریخ share: topic: 'پیوندی به این موضوع را به اشتراک بگذارید' post: 'ارسال #%{postNumber}' @@ -92,27 +93,51 @@ fa_IR: google+: 'این پیوند را در Google+‎ به اشتراک بگذارید.' email: 'این پیوند را با ایمیل بفرستید' action_codes: - split_topic: "این مبحث را جدا کنید %{when}" + public_topic: "این موضوع در %{when} عمومی شده" + private_topic: "این موضوع در %{when} خصوصی شده" + split_topic: "این موضوع را در %{when} جدا کنید " + invited_user: "%{who} در %{when} دعوت شده" + invited_group: "%{who} در %{when} دعوت شده" + removed_user: "%{who} در %{when} حذف شد" + removed_group: "%{who} در %{when} حذف شد" autoclosed: - enabled: 'بسته شد %{when}' - disabled: 'باز شد %{when}' + enabled: 'در %{when} بسته شد' + disabled: 'در %{when} باز شد ' closed: - enabled: 'بسته شد %{when}' - disabled: 'باز شد %{when}' + enabled: 'در %{when} بسته شد' + disabled: 'در %{when} باز شد' archived: - enabled: 'بایگانی شد %{when}' - disabled: 'از بایگانی درآمد %{when}' + enabled: 'در %{when} بایگانی شد' + disabled: 'در %{when} از بایگانی درآمد' pinned: - enabled: 'سنجاق شد %{when}' - disabled: 'از سنجاق خارج شد %{when}' + enabled: 'در %{when} سنجاق شد' + disabled: 'در %{when} از سنجاق خارج شد' pinned_globally: - enabled: 'به صورت سراسری سنجاق شد %{when}' - disabled: 'از سنجاق خارج شد %{when}' + enabled: 'در %{when} به صورت سراسری سنجاق شد' + disabled: 'در %{when} از سنجاق خارج شد' visible: - enabled: 'فهرست شد %{when}' - disabled: 'از فهرست پاک شد %{when}' + enabled: 'در %{when} فهرست شد' + disabled: 'در %{when} از فهرست پاک شد' topic_admin_menu: "اقدامات مدیریت موضوع" emails_are_disabled: "تمام ایمیل های خروجی بصورت کلی توسط مدیر قطع شده است. هیچگونه ایمیل اگاه سازی ارسال نخواهد شد." + themes: + default_description: "پیش‌فرض" + s3: + regions: + us_east_1: "شرق ایالات متحده (ویرجینیا شمالی)" + us_west_1: "غرب ایالات متحده (کالیفرنیا شمالی)" + us_west_2: "غرب ایالات متحده (اورگان)" + us_gov_west_1: "AWS GovCloud (ایالات متحده)" + eu_west_1: "اروپا (ایرلند)" + eu_west_2: "اروپا (لندن)" + eu_central_1: "اروپا (فرانکفورت)" + ap_southeast_1: "آسیا و اقیانوسیه (سنگاپور)" + ap_southeast_2: "آسیا و اقیانوسیه (سیدنی)" + ap_south_1: "آسیا و اقیانوسیه (بمبئی)" + ap_northeast_1: "آسیا و اقیانوسیه (توکیو)" + ap_northeast_2: "آسیا و اقیانوسیه (سئول)" + sa_east_1: "آمریکای جنوبی (سائوپائولو)" + cn_north_1: "چین (پکن)" edit: 'سرنویس و دستهٔ این موضوع را ویرایش کنید' not_implemented: "آن ویژگی هنوز به کار گرفته نشده، متأسفیم!" no_value: "نه" @@ -144,6 +169,8 @@ fa_IR: more: "بیشتر" less: "کمتر" never: "هرگز" + every_30_minutes: "هر 30 دقیقه" + every_hour: "هر ساعت" daily: "روزانه" weekly: "هفتگی" every_two_weeks: "هر دو هفته" @@ -154,6 +181,7 @@ fa_IR: other: "{{count}} نویسه" suggested_topics: title: "موضوعات پیشنهادی" + pm_title: "پیام‌های پیشنهادی" about: simple_title: "درباره" title: "درباره %{title}" @@ -167,6 +195,7 @@ fa_IR: like_count: "لایک ها " topic_count: "موضوعات" post_count: "پست ها" + user_count: "کاربران" active_user_count: "کاربران فعال" contact: "ارتباط با ما" contact_info: "در شرایط حساس و مسائل اضطراری مربوط به سایت٬‌ لطفا با تماس بگیرید از طریق %{contact_info}." @@ -204,6 +233,8 @@ fa_IR: undo: "برگردانی" revert: "برگشت" failed: "ناموفق" + switch_to_anon: "ورود به حالت ناشناس" + switch_from_anon: "خروج از حالت ناشناس" banner: close: "این سردر را رد بده." edit: "این بنر را ویرایش کنید >>" @@ -225,6 +256,7 @@ fa_IR: has_pending_posts: other: "این عنوان دارای {{count}} نوشته‌ی در انتظار تایید است" confirm: "ذخیره سازی تغییرها" + delete_prompt: "آیا از حذف %{username} اطمینان دارید؟ این عمل باعث حذف شدن تمامی نوشته های او و همینطور باعث مسدود شدن IP و ایمیل او نیز می شود." approval: title: "نوشته نیاز به تایید دارد" description: "ما نوشته شما را دریافت کرده ایم ولی نیاز به تایید آن توسط یکی از مدیران است قبل از اینکه نمایش داده شود. لطفا صبر داشته باشید." @@ -250,6 +282,8 @@ fa_IR: title: "کاربران" likes_given: "داده" likes_received: "دریافت" + topics_entered: "دیده شده" + topics_entered_long: "موضوعات دیده شده" time_read: "زمان خوانده‌ شده" topic_count: "موضوعات" topic_count_long: "موضوعات ساخته شده" @@ -262,17 +296,68 @@ fa_IR: posts_read_long: "خواندن نوشته ها" total_rows: other: "%{count} کاربران" + group_histories: + actions: + change_group_setting: "تغییر تنظیمات گروه" + add_user_to_group: "افزودن کاربر" + remove_user_from_group: "حذف کاربر" + make_user_group_owner: "مدیر کردن" + remove_user_as_group_owner: "ابطال مدیریت" groups: + logs: + title: "گزارش ها" + when: "چه زمانی" + action: "عمل" + acting_user: "کاربر اجرایی" + target_user: "کاربر مقصد" + subject: "موضوع" + details: "جزئیات" + from: "از" + to: "به" + edit: + title: 'ویرایش گروه' + full_name: 'نام و نام خانوادگی' + add_members: "افزودن اعضا" + delete_member_confirm: "کاربر '%{username}' از گروه '%{group}' حذف گردد؟" + request_membership_pm: + title: "درخواست عضویت" + body: "من تقاضای عضویت در @%{groupName} را دارم." + name_placeholder: "در نام گروه نمی توان از فاصله استفاده کرد، مانند قوانین نامگذاری نام کاربری" + public: "اجازه‌ی ورود/خروج کاربران به گروه (نیازمند قابل نمایش بودن گروه است)" + empty: + posts: "در این گروه هیچ نوشته ای توسط کاربران ارسال نشده." + members: "این گروه هیچ عضوی ندارد" + mentions: "هیچ اشاره ای به این گروه وجود ندارد." + messages: "هیچ پیامی برای این گروه وجود ندارد." + topics: "در این گروه هیچ موضوعی توسط اعضای آن ایجاد نشده." + logs: "هیچ گزارشی برای این گروه موجود نیست." add: "افزودن" + join: "پیوستن به گروه" + leave: "ترک گروه" + request: "درخواست پیوستن به گروه" + automatic_group: گروه خودکار + closed_group: گروه بسته شده + is_group_user: "شما عضو این گروه هستید" + allow_membership_requests: "اجازه ارسال درخواست عضویت در گروه به کاربران" + membership: "عضویت" + name: "نام" + user_count: "تعداد اعضا" + bio: "درباره گروه" selector_placeholder: "افزودن عضو" owner: "مالک" visible: "همهٔ کاربران گروه را می‌بینند" index: title: "گروه‌ها" + empty: "هیچ گروه قابل نمایشی وجود ندارد." title: other: "گروه‌ها" + activity: "فعالیت" members: "اعضا" + topics: "موضوعات" posts: "نوشته ها" + mentions: "نام برده شده‌ها" + messages: "پیام‌ها" + notification_level: "سطح آگاه‌سازی پیش‌فرض برای پیام‌های گروهی" alias_levels: title: "چه کسی میتواند پیام بفرستد و به این گروه @اشاره کند؟" nobody: "هیچ‌کس" @@ -281,14 +366,17 @@ fa_IR: members_mods_and_admins: "تنها کاربران گروه، مدیران ومدیران کل" everyone: "هرکس" trust_levels: - title: "سطح اعتماد به صورت خودکار به اعضاء داده میشود وقتی که آن ها اضافه میشوند:" + title: "سطح اعتماد به صورت خودکار به اعضایی که اضافه می‌شوند داده می‌شود:" none: "هیچ کدام" notifications: watching: title: "در حال مشاهده" - description: "در صورت ارسال شدن پست جدید در هر پیام یک اعلان برای شما ارسال می‌شود و تعداد پاسخ‌های جدید نمایش داده می‌شود." + description: "در صورت ارسال شدن پست جدید در هر پیام یک اعلان برای شما ارسال می‌شود و تعداد پاسخ‌های جدید در آن نمایش داده می‌شود." + watching_first_post: + title: "در حال مشاهده نوشته اول" + description: "تنها برای اولین نوشته در هر مبحث جدید به شما اطلاع رسانی خواهد شد." tracking: - title: "ردگیری" + title: "پیگیری" description: "در صورت اشاره شدن به @نام شما توسط اشخاص دیگر و یا دریافت پاسخ، اعلانی برای شما ارسال می‌شود و تعداد پاسخ‌های جدید نمایش داده می‌شود." regular: title: "معمولی" @@ -296,6 +384,15 @@ fa_IR: muted: title: "بی صدا شد" description: "با ارسال شدن موضوعات جدید در این گروه شما اعلانی دریافت نمی‌کنید." + flair_url: "تصویر آواتار" + flair_url_placeholder: "(اختیاری) URL تصویر و یا کلاس فونت Awesome " + flair_bg_color: "رنگ پس زمینه آواتار" + flair_bg_color_placeholder: "(اختیاری) کد HEX رنگ" + flair_color: "رنگ آواتار" + flair_color_placeholder: "(اختیاری) کد HEX رنگ" + flair_preview_icon: "پیش نمایش آیکن" + flair_preview_image: "پیش‌نمایش تصویر" + flair_note: "نکته:‌ خصیصه فقط برای گروه اصلی کاربر نمایش داده می‌شود." user_action_groups: '1': "پسندهای داده شده" '2': "پسندهای دریافت شده" @@ -317,7 +414,7 @@ fa_IR: category_list: "نمایش لیست دسته‌بندی" reorder: title: "دوباره مرتب کردن دسته بندی ها" - title_long: "سازماندهی مجدد فهرست دسته بندی ها" + title_long: "سازماندهی مجدد فهرست دسته بندی‌ها" fix_order: "اصلاح موقعیت ها" fix_order_tooltip: "همه دسته بندی ها یک شماره موقعیت مخصوص ندارند, که ممکن است باعث نتایج غیر منتظره شود." save: "ذخیره ترتیب" @@ -329,6 +426,8 @@ fa_IR: latest_by: "آخرین توسط" toggle_ordering: "ضامن کنترل مرتب سازی" subcategories: "زیر دسته‌ بندی ها" + topic_sentence: + other: "%{count} مبحث" topic_stat_sentence: other: "%{count} موضوعات تازه در %{unit} گذشته." ip_lookup: @@ -353,6 +452,11 @@ fa_IR: profile: "نمایه" mute: "بی صدا" edit: "ویرایش تنظیمات" + download_archive: + button_text: "دانلود نوشته های من" + confirm: "آیا مطمئنید که می‌خواهید نوشته‌هایتان را دانلود کنید؟" + success: "شروع فرایند دانلود، وقتی این فرایند تکمیل شود به شما از طریق پیام، اطلاع رسانی خواهد شد." + rate_limit_error: "نوشته ها را می توانید فقط روزی یک بار دانلود کنید. لطفا فردا دوباره امتحان کنید." new_private_message: "پیام های جدید" private_message: "پیام" private_messages: "پیام‌ها" @@ -370,10 +474,13 @@ fa_IR: not_supported: "اعلانات بر روی این مرورگر پشتیبانی نمیشوند. با عرض پوزش." perm_default: "فعال کردن اعلانات" perm_denied_btn: "دسترسی رد شد" + perm_denied_expl: "شما دسترسی دریافت پیام را بسته اید. در تنظیمات مرورگر خود آنرا فعال کنید." disable: "غیرفعال کردن اعلانات" enable: "فعال کردن اعلانات" each_browser_note: "نکته: شما باید این تنظیمات را در هر مرورگری که استفاده میکنید تغییر دهید." + dismiss_notifications: "پنهان کردن همه" dismiss_notifications_tooltip: "علامت گذاری همه اطلاعیه های خوانده نشده به عنوان خوانده شده" + first_notification: "اولین پیام اطلاع رسانی شما! برای شروع آنرا انتخاب کنید." disable_jump_reply: "بعد از پاسخ من به پست من پرش نکن" dynamic_favicon: " تعداد موضوعات جدید یا بروز شده را روی آیکون مرورگر نمایش بده" external_links_in_new_tab: "همهٔ پیوندهای برون‌رو را در یک تب جدید باز کن" @@ -387,8 +494,24 @@ fa_IR: suspended_notice: "این کاربر تا {{date}} در وضعیت معلق است." suspended_reason: "دلیل: " github_profile: "Github" + email_activity_summary: "چکیدهٔ فعالیت" + mailing_list_mode: + label: "حالت لیست ایمیل" + enabled: "فعالسازی حالت لیست ایمیل" + daily: "ارسال بروزر‌سانی روزانه" + individual: "ارسال یک ایمیل برای هر پست جدید" + individual_no_echo: "ارسال ایمیل برای هر پست جدید، به جز پست‌های خودم" + few_per_day: "ارسال یک ایمیل برای هر پست جدید (حدود 2 ایمیل در روز)" + tag_settings: "برچسب‌ها" + watched_tags: "تماشا شده" + tracked_tags: "پی‌گیری شده" + tracked_tags_instructions: "شما به صورت خودکار تمام عناوین جدید در این برچسب را پیگیری خواهید کرد. تعداد نوشته های جدید در کنار عنواین نمایش داده می‌شود." + muted_tags: "بی صدا شده" watched_categories: "تماشا شده" tracked_categories: "پی‌گیری شده" + watched_first_post_categories: "در حال مشاهده نوشته اول" + watched_first_post_categories_instructions: "برای اولین پست موضوعات جدید به شما اطلاع‌رسانی می‌شود" + watched_first_post_tags: "درحال مشاهده نوشته اول" muted_categories: "بی صدا شد" muted_categories_instructions: "شما از هیچ چیز مباحث جدید این دسته بندی ها آگاه نمیشوید, و آن ها در آخرین ها نمایش داده نمیشوند." delete_account: "حساب من را پاک کن" @@ -401,6 +524,14 @@ fa_IR: muted_users: "بی صدا شده" muted_users_instructions: "متفوقف کردن تمام اطلاعیه ها از طرف این کاربران." muted_topics_link: "نمایش مباحث قطع شده" + watched_topics_link: "نمایش موضوعات مشاهده‌شده" + tracked_topics_link: "نمایش موضوعات پی‌گیری شده" + automatically_unpin_topics: "برداشتن پین موضوعات وقتی به پایین صفحه رسیدم." + apps: "برنامه‌ها" + revoke_access: "لغو دسترسی" + undo_revoke_access: "بازنشانی لغو دسترسی" + api_approved: "تایید شده:" + theme: "قالب" staff_counters: flags_given: "پرچم گذاری های مفید" flagged_posts: "نوشته های پرچم گذاری شده" @@ -415,6 +546,7 @@ fa_IR: groups: "گروه های من" bulk_select: "انتخاب پیام‌ها" move_to_inbox: "انتقال به صندوق دریافت" + move_to_archive: "بایگانی" failed_to_move: "انتقال پیام‌های انتخاب شده با اشکال مواجه شد (شاید اتصال شما در دسترس نیست)" select_all: "انتخاب همه" change_password: @@ -423,8 +555,11 @@ fa_IR: error: "(خطا)" action: "ارسال ریست رمز عبور به ایمیل " set_password: "تغییر کلمه عبور" + choose_new: "یک رمز عبور جدید وارد کنید" + choose: "یک رمز عبور وارد کنید" change_about: title: "تغییر «دربارهٔ من»" + error: "در فرآیند تغییر این مقدار خطایی روی داد." change_username: title: "تغییر نام کاربری" taken: "متأسفیم، آن نام کاربری قبلا گرفته شده است." @@ -455,6 +590,7 @@ fa_IR: instructions: "تصاویر پس زمینه در مرکز قرار خواهند گرفت و عرض پیشفرض آن 590 پیکسل است" email: title: "ایمیل" + instructions: "به عموم مردم نشان داده نشده است" ok: "ایمیلی برای تایید برایتان می‌فرستیم" invalid: "لطفا یک آدرس ایمیل معتبر وارد کنید" authenticated: "ایمیل شما تصدیق شد توسط {{provider}}" @@ -463,14 +599,17 @@ fa_IR: other: "ما فقط در صورتی برای شما ایمیل میفرستیم که شما را در {{count}} دقیقه آخر ندیده باشیم." name: title: "نام" + instructions: "نام و نام‌خانوادگی شما" instructions_required: "نام کامل شما" too_short: "نام انتخابی شما خیلی کوتاه است" ok: "نام انتخابی شما به نطر می رسد خوب است" username: title: "نام کاربری" + instructions: "یکتا، بدون فاصله، کوتاه" short_instructions: "می توانید به کاربران دیگر اشاره کنید با@{{username}}" available: "نام کاربری شما موجود است" not_available: "فراهم نیست. این را امتحان کن {{suggestion}} ؟" + not_available_no_suggestion: "در دسترس نیست" too_short: "نام کاربری انتخابی شما خیلی کوتاه است" too_long: "نام کاربری انتخابی شما بسیار بلند است" checking: "بررسی فراهمی نام‌کاربری..." @@ -479,6 +618,7 @@ fa_IR: title: "زبان رابط کاربر" instructions: "زبان رابط کاربری. با تازه کردن صفحه تغییر خواهد کرد." default: "(پیش‌فرض)" + any: "هر" password_confirmation: title: "رمز عبور را مجدد وارد نمایید" last_posted: "آخرین نوشته" @@ -491,7 +631,18 @@ fa_IR: title: "کارت مدال کاربر" website: "تارنما" email_settings: "ایمیل" + like_notification_frequency: + title: "وقتی پسندیده شد خبر بده" + always: "همیشه" + first_time_and_daily: "اولین بار که پست پسندیده شد و روزانه" + first_time: "اولین بار که پست پسندیده شد" + never: "هیچوقت" + email_previous_replies: + always: "همیشه" + never: "هیچوقت" email_digests: + every_30_minutes: "هر 30 دقیقه" + every_hour: "هر ساعت" daily: "روزانه" every_three_days: "هر سه روز" weekly: "هفتگی" @@ -541,12 +692,15 @@ fa_IR: rescind: "پاک کردن" rescinded: "فراخوانه پاک شد" reinvite: "ارسال دوباره دعوت" + reinvite_all: "ارسال مجدد تمام دعوت‌ها" reinvited: "فرستادن دوباره دعوتنامه" + reinvited_all: "تمام دعوت‌ها دوباره ارسال شدند!" time_read: "زمان خواندن" days_visited: "روز های بازدید شده" account_age_days: "عمر حساب بر اساس روز" create: "فرستادن یک دعوتنامه" generate_link: "کپی لینک دعوت" + link_generated: "لینک دعوت با موفقیت ایجاد شد!" bulk_invite: text: "دعوت گروهی از طریق فایل" success: "فایل با موفقیت بارگذاری شد٬ وقتی که پروسه تمام شد به شما را از طریق پیام اطلاع می دهیم. " @@ -558,9 +712,38 @@ fa_IR: same_as_email: "رمز عبورتان با ایمیل شما برابر است. " ok: "گذرواژهٔ خوبی است." summary: + title: "خلاصه" + stats: "آمار" + time_read: " زمان خواندن" + topic_count: + other: "موضوعات ساخته شدند" + post_count: + other: "پست‌ها ساخته شدند" + likes_given: + other: " داده" + likes_received: + other: " دریافت کرده" + days_visited: + other: "روز‌های دیده شده" + posts_read: + other: "پست‌های خوانده شده" + bookmark_count: + other: "نشانک ها" + top_replies: "بالا‌ترین پاسخ‌ها" + no_replies: "بدون پاسخ" + more_replies: "پاسخ‌های بیشتر" + top_topics: "برترین موضوعات" + no_topics: "بدون موضوع." + more_topics: "موضوعات بیشتر" top_badges: "مدال های برتر" no_badges: "هنوز مدالی نیست." more_badges: "مدال های بیشتر" + top_links: "برترین لینک‌ها" + no_links: "بدون لینک" + most_liked_by: "بیشترین پسندیده شده توسط" + most_liked_users: "بیشترین پسندیده شده" + most_replied_to_users: "بیشترین پاسخ به" + no_likes: "بدون پسندیده شدن." associated_accounts: "ورود ها" ip_address: title: "آخرین نشانی IP" @@ -603,11 +786,15 @@ fa_IR: logout: "شما از سایت خارج شده اید" refresh: "تازه کردن" read_only_mode: + enabled: "سایت در حال فقط خواندنی است. می‌توانید موضوعات را مشاهده کنید ولی امکان ارسال پاسخ، پسندیدن و سایر عملیات در حال حاضر غیر‌فعال است." login_disabled: "ورود به سیستم غیر فعال شده همزمان با اینکه سایت در حال فقط خواندنی است." + logout_disabled: "سایت در حال فقط خواندنی است و امکان خروج در این حالت وجود ندارد." too_few_topics_and_posts_notice: "بیا این بحث را شروع کنیم! در حال حاضر این %{currentTopics} / %{requiredTopics} مبحث ها و %{currentPosts} / %{requiredPosts} ارسال ها وجود دارد. بازدید کنندگان تازه وارد نیاز دارند به گفتگو هایی برای خواندن و پاسخ دادن." too_few_topics_notice: "بیا این بحث را شروع کنیم! در حال حاضر این %{currentTopics} / %{requiredTopics} مباحث وجود دارد. بازدیدکنندگان تازه وارد نیاز دارند به گفتگوهایی برای خواندن و پاسخ دادن." too_few_posts_notice: "بیا این بحث را شروع کنیم! در حال حاضر این %{currentPosts} / %{requiredPosts} ارسال ها وجود دارد. بازدیدکنندگان تازه وارد نیاز دارند به گفتگوهایی برای خواندن و پاسخ دادن." learn_more: "بیشتر بدانید..." + all_time: 'مجموع' + all_time_desc: 'مجموع موضوعات ایجاد شده' year: 'سال' year_desc: 'موضوعاتی که در 365 روز گذشته باز شده‌اند' month: 'ماه' @@ -682,6 +869,10 @@ fa_IR: not_allowed_from_ip_address: "شما نمی توانید با این اپی ادرس وارد شوید." admin_not_allowed_from_ip_address: "شما نمی تواند با این اپی آدرس وارد کنترل پنل ادمین شوید." resend_activation_email: "برای فرستادن دوبارهٔ رایانامهٔ‌فعال‌سازی، اینجا را بفشارید." + resend_title: "ارسال مجدد ایمیل فعال‌سازی" + change_email: "تغییر ایمیل" + provide_new_email: "با تغییر آدرس ایمیل، ایمیل فعال‌سازی مجددا برای شما ارسال می‌شود" + submit_new_email: "تغییر ایمیل" sent_activation_email_again: "رایانامهٔ‌ فعال‌سازی دیگری را برایتان به نشانی {{currentEmail}} فرستادیم. چند دقیقه‌ای طول می‌کشد تا برسد. مطمئن شوید که پوشهٔ هرزنامه را بررسی می‌کنید." to_continue: "لطفا وارد شوید" preferences: "شما باید وارد شوید تا بتوانید تنظیمات کاربری خود را تغییر بدهید." @@ -695,6 +886,9 @@ fa_IR: twitter: title: "با Twitter" message: "اعتبارسنجی با توئیتر (مطمئن شوید که بازدارنده‌های pop up فعال نباشند)" + instagram: + title: "با اینستاگرام" + message: "ورود با اینستاگرام (مطمئن شوید که از افزونه‌های popup blocker استفاده نمی‌کنید)" facebook: title: "با Facebook" message: "اعتبارسنجی با فیسبوک (مطمئن شوید که بازدارنده‌های pop up فعال نباشند)" @@ -704,16 +898,40 @@ fa_IR: github: title: "با GitHub" message: "اعتبارسنجی با گیت‌هاب (مطمئن شوید که بازدارنده‌های pop up فعال نباشند)" + invites: + accept_title: "دعوت‌نامه" + welcome_to: "به %{site_name} خوش آمدید!" + invited_by: "توسط این شخص دعوت شدید:" + social_login_available: "شما می‌توانید به شبکه‌های اجتماعی که از ایمیل فعلی شما استفاده می‌کنند وارد شوید." + your_email: "ایمیل شما: %{email}." + accept_invite: "پذیرفتن دعوت" + success: "عضویت با موفقیت انجام شد و می‌توانید وارد سایت شوید." + password_label: "تنظیم رمز (اختیاری)" + password_reset: + continue: "برو به %{site_name}" + emoji_set: + apple_international: "اپل/جهانی" + google: "گوگل" + twitter: "توییتر" + emoji_one: "ایموجی وان" + win10: "ویندوز 10" + category_page_style: + categories_only: "فقط دسته‌بندی‌ها" + categories_with_featured_topics: "دسته‌بندی هایی که دارای موضوعات برجسته هستند" + categories_and_latest_topics: "دسته‌بندی‌ها و آخرین موضوعات" shortcut_modifier_key: shift: 'Shift' ctrl: 'Ctrl' alt: 'Alt' composer: + emoji: "شکلک :)" more_emoji: "بیشتر..." options: "گزینه ها" whisper: "نجوا" + unlist: "از فهرست پاک شده" add_warning: "این یک هشدار رسمی است." toggle_whisper: "تغییر وضعیت نجوا" + toggle_unlisted: "تعویض خروج از لیست" posting_not_on_topic: "به کدام موضوع می‌خواهید پاسخ دهید؟" saving_draft_tip: "در حال ذخیره سازی ..." saved_draft_tip: "اندوخته شد" @@ -740,6 +958,7 @@ fa_IR: title_placeholder: "در یک جملهٔ‌ کوتاه، این موضوع در چه موردی است؟" edit_reason_placeholder: "چرا ویرایش می‌کنید؟" show_edit_reason: "(افزودن دلیل ویرایش)" + topic_featured_link_placeholder: "پیوندی که با عنوان نمایش داده می‌شود وارد کنید." reply_placeholder: "اینجا بنویسید. از Markdown, BBCode و یا HTML برای شکل دادن استفاده کنید. عکس ها را بکشید و یا کپی کنید." view_new_post: "نوشته تازه‌تان را ببینید." saving: "در حال ذخیره سازی" @@ -749,8 +968,10 @@ fa_IR: show_preview: 'نشان دادن پیش‌نمایش «' hide_preview: '» پنهان کردن پیش‌نمایش' quote_post_title: "نقل‌قول همهٔ‌ نوشته" + bold_label: "B" bold_title: "زخیم" bold_text: "نوشته قوی " + italic_label: "I" italic_title: "تاکید" italic_text: "متن تاکید شده" link_title: "لینک ارتباط دار" @@ -761,11 +982,13 @@ fa_IR: quote_text: "نقل قول" code_title: "نوشته تنظیم نشده" code_text: "متن تورفتگی تنظیم نشده توسط 4 فضا خالی" + paste_code_text: "کد را در اینجا بنویسید یا بچسبانید" upload_title: "بارگذاری" upload_description: "توضیح بارگذاری را در اینجا بنویسید" olist_title: "لیست شماره گذاری شد" ulist_title: "لیست بولت" list_item: "فهرست موارد" + heading_label: "H" heading_title: "عنوان" heading_text: "عنوان" hr_title: "خط کش افقی" @@ -774,19 +997,14 @@ fa_IR: modal_ok: "باشه" modal_cancel: "لغو کردن" cant_send_pm: "متاسفانه , شما نمیتوانید پیام بفرستید به %{username}." + yourself_confirm: + title: "آیا اضافه کردن دریافت کنندگاه را فراموش کردید؟" + body: "در حال حاضر این پیام فقط به خود شما ارسال می‌شود!" admin_options_title: "تنظیمات اختیاری مدیران برای این موضوع" - auto_close: - label: "بستن خودکار موضوع در زمان :" - error: "لطفا یک مقدار معتبر وارد نمایید." - based_on_last_post: "آیا تا آخرین نوشته یک موضوع بسته نشده در این قدیمی است." - all: - examples: 'عدد ساعت را وارد نمایید (24)،زمان کامل (17:30) یا برچسب زمان (2013-11-22 14:00).' - limited: - units: "(# از ساعت ها)" - examples: 'لطفا عدد ساعت را وارد نمایید (24).' notifications: title: "اطلاع رسانی با اشاره به @name ،پاسخ ها به نوشته ها و موضوعات شما،پیام ها ، و ..." none: "قادر به بار گذاری آگاه سازی ها در این زمان نیستیم." + empty: "اطلاعیه‌ای پیدا نشد." more: "دیدن آگاه‌سازی‌های پیشن" total_flagged: "همهٔ نوشته‌های پرچم خورده" mentioned: "

    {{username}} {{description}}

    " @@ -841,10 +1059,12 @@ fa_IR: sort_by: "مرتب سازی بر اساس" relevance: "ارتباطات" latest_post: "آخرین ارسال" + latest_topic: "آخرین موضوعات" most_viewed: "بیشترین بازدید شده" most_liked: "بیشترین پسندیده شده" select_all: "انتخاب همه" clear_all: "پاک کردن همه" + too_short: "کلمه جستجو شده بسیار کوتاه است" result_count: other: "{{count}} نتایج \"{{term}}\"" title: "جستجوی موضوعات، نوشته ها، کاربران یا دسته‌ بندی ها" @@ -856,13 +1076,56 @@ fa_IR: user: "جستجوی نوشته‌ها با @{{username}}" topic: "جستجوی این موضوع" private_messages: "جستجوی پیام" + advanced: + title: جستجوی پیشرفته + posted_by: + label: فرستنده + in_category: + label: در دسته‌بندی + in_group: + label: در گروه + with_badge: + label: با نشان + with_tags: + label: با برچسب + filters: + label: فقط موضوعات/پست‌هایی را برگردان که... + likes: پسندیدم + posted: پست ارسال کردم + watching: در حال مشاهده هستم + tracking: در حال پیگیری هستم + private: در پیام‌های من هستند + bookmarks: نشانه گذاری کردم + first: اولین پست هستند + pinned: سنجاق شدند + unpinned: سنجاق نشدند + seen: خواندم + unseen: نخواندم + wiki: دانشنامه هستند + statuses: + label: موضوع بودند + open: باز هستند + closed: بسته شده هستند + archived: بایگانی شده هستند + noreplies: بدون پاسخ هستند + single_user: شامل یک کاربر هستند + post: + count: + label: حداقل تعداد پست + time: + label: ارسال شده + before: قبل + after: بعد hamburger_menu: "به فهرست مبحث یا دسته بندی دیگر بروید" new_item: "تازه" go_back: 'برگردید' not_logged_in_user: 'صفحه کاربر با خلاصه ای از فعالیت های و تنظیمات' current_user: 'به نمایه‌تان بروید' topics: + new_messages_marker: "آخرین بازدید" bulk: + select_all: "انتخاب همه" + clear_all: "پاکسازی همه" unlist_topics: "از فهرست خارج کردن مباحث" reset_read: "تنظیم مجدد خوانده شد" delete: "حذف موضوعات" @@ -881,6 +1144,11 @@ fa_IR: choose_new_category: "یک دسته بندی جدید برای موضوع انتخاب نمایید" selected: other: "شما تعداد {{count}} موضوع را انتخاب کرده اید." + change_tags: "جایگزینی برچسب‌ها" + append_tags: "افزودن برچسب‌ها" + choose_new_tags: "انتخاب برچسب‌های جدید برای این موضوعات:" + choose_append_tags: "انتخاب برچسب‌های جدید برای افزودن به این موضوعات:" + changed_tags: "انتخاب برچسب برای موضوعاتی که عوض شدند" none: unread: "موضوع خوانده نشده‌ای ندارید." new: "شما هیچ موضوع تازه‌ای ندارید" @@ -909,9 +1177,17 @@ fa_IR: unsubscribe: stop_notifications: "حالا شما اعلانات کمتری برای {{title}} دریافت میکنید" change_notification_state: "وضعیت فعلی اعلانات شما این هست" + filter_to: + other: "{{count}} نوشته در این موضوع وجود دارد" create: 'موضوع جدید' create_long: 'ساخت یک موضوع جدید' private_message: 'شروع یک پیام' + archive_message: + help: 'انتقال پیام به بایگانی' + title: 'بایگانی' + move_to_inbox: + title: 'انتقال به صندوق دریافت' + help: 'بازگرداندن پیام به صندوق دریافتی' list: 'موضوعات' new: 'موضوع تازه' unread: 'خوانده نشده' @@ -951,31 +1227,62 @@ fa_IR: jump_reply_up: رفتن به جدید ترین پاسخ jump_reply_down: رفتن به آخرین پاسخ deleted: "موضوع پاک شده است." - auto_close_notice: "این موضوع خودکار بسته خواهد شد %{timeLeft}." - auto_close_notice_based_on_last_post: "این نوشته بعد از %{duration} آخرین پاسخ بشته خواهد شد ." + topic_status_update: + title: "تنظیم زمان سنج موضوع" + save: "تنظیم زمان سنج" + num_of_hours: "تعداد ساعت:" + remove: "حذف زمان سنج" + publish_to: "انتشار در:" + when: "در زمان:" + auto_update_input: + later_today: "امروز" + tomorrow: "فردا" + later_this_week: "این هفته" + this_weekend: "آخر هفته" + next_week: "هفته بعد" + pick_date_and_time: "انتخاب زمان و تاریخ" + set_based_on_last_post: "بستن با توجه به آخرین پست" + publish_to_category: + title: "زمان‌بندی انتشار" + temp_open: + title: "باز کردن موقتی" + auto_reopen: + title: "باز کردن خودکار موضوع" + temp_close: + title: "بستن موقتی" + auto_close: + title: "بستن خودکار موضوع" + label: "مقدار زمان بسته شدن خودکار موضوع:" + error: "یک مقدار معتبر وارد کنید." + based_on_last_post: "تا زمانی آخرین پست موضوع این قدر قدیمی نشده، موضوع را نبند." + status_update_notice: + auto_open: "این موضوع به صورت خودکار در %{timeLeft} باز خواهد شد." + auto_close: "این موضوع به صورت خودکار در %{timeLeft} بسته خواهد شد." auto_close_title: 'تنضیمات قفل خوکار' - auto_close_save: "‌ذخیره" - auto_close_remove: "این موضوع را خوکار قفل نکن" + timeline: + back: "بازگشت" + back_description: "بازگشت به اولین پست خوانده نشده" + replies_short: "%{current} از %{total}" progress: title: نوشته ی در حال اجرا go_top: "بالا" go_bottom: "پایین" go: "برو" jump_bottom: "پرش به آخرین نوشته" + jump_prompt: "پرش به..." + jump_prompt_of: "از %{count} نوشته" + jump_prompt_long: "می‌خواهید به کدام نوشته منتقل شوید؟" jump_bottom_with_number: "رفتن به نوشته ی %{post_number}" total: همهٔ نوشته‌ها current: نوشته کنونی notifications: + title: بازه زمانی آگاه‌سازی از این موضوع را تغییر دهید reasons: '3_6': 'شما آگاه‌سازی‌ها را دریافت خواهید کرد، زیرا شما در حال مشاهده ی این دسته بندی هستید.' '3_5': 'شما آگاه‌سازی‌ها را دریافت خواهید کرد، زیرا تماشای خودکار این موضوع را آغاز کرده‌اید.' '3_2': 'شما آگاه سازی دریافت می کنید زیرا در حال مشاهده این جستار هستید.' '3_1': 'از آنجا که این موضوع را ساخته‌اید، از رویدادهای آن آگاه خواهید شد.' '3': 'از آنجا که این موضوع را تماشا می‌کنید، از رویدادهای آن آگاه خواهید شد.' - '2_8': 'شما آگاه سازی دریافت خواهید کرد چرا که شما این دسته بندی را پی گیری می کنید.' - '2_4': 'از آنجا که به این جستار پاسخ فرستادید، از رویدادهای آن آگاه خواهید شد.' - '2_2': 'از آنجا که این موضوع را دنبال می‌کنید، از رویدادهای آن آگاه خواهید شد.' - '2': 'شما اطلاعیه ای دریافت خواهید کرد چون این موضوع را مطالعه می نمایید.' '1_2': 'در صورتی که فردی با @name به شما اشاره کند یا به شما پاسخی دهد به شما اطلاع داده خواهد شد.' '1': 'در صورتی که فردی با @name به شما اشاره کند یا به شما پاسخی دهد به شما اطلاع داده خواهد شد.' '0_7': 'شما تمام آگاه سازی های این دسته بندی را نادیده گرفته اید' @@ -1011,7 +1318,6 @@ fa_IR: open: "باز کردن موضوع " close: "بستن موضوع" multi_select: "گزیدن دیدگاه‌ها..." - auto_close: "بستن خودکار" pin: "سنجاق زدن جستار..." unpin: "برداشتن سنجاق جستار..." unarchive: "موضوع بایگانی نشده" @@ -1019,18 +1325,26 @@ fa_IR: invisible: "خارج کردن از لیست" visible: "فهرست ساخته شد" reset_read: "تنظیم مجدد خواندن داده ها" + make_public: "ایجاد موضوع عمومی" + make_private: "ایجاد پیام خصوصی" feature: pin: "سنجاق زدن جستار" unpin: "برداشتن سنجاق جستار" pin_globally: "سنجاق کردن موضوع در سطح سراسری" make_banner: "اعلان موضوع" remove_banner: "حذف اعلان موضوع" + reply: + title: 'پاسخ' + help: 'شروع ارسال پاسخ به این موضوع' clear_pin: title: "برداشتن سنجاق" help: "سنجاق استاتوس این موضوع را بردارید که پس از آن دیگر این موضوع در بالای فهرست موضوعات شما دیده نمی‌شود." share: title: 'همرسانی ' help: 'همرسانی یک پیوند برای این موضوع' + print: + title: 'چاپ' + help: 'باز کردن حالت قابل چاپ موضوع' flag_topic: title: 'پرچم' help: 'پرچم خصوصی برای این موضوع جهت توجه یا برای ارسال آگاه سازی شخصی در باره آن.' @@ -1060,12 +1374,14 @@ fa_IR: no_banner_exists: "هیچ مبحث سرصفحه ای وجود ندارد." banner_exists: "یک مبحث سرصفحه ای هست در حال حاضر." inviting: "فراخوانی..." + automatically_add_to_groups: "این دعوت شامل دسترسی به این گروه‌ها نیز هست:" invite_private: title: 'دعوت به پیام خصوصی' email_or_username: "دعوتنامه ی ایمیل یا نام کاربر" email_or_username_placeholder: "نشانی ایمیل یا نام کاربری" action: "دعوتنامه " success: "ما آن کاربر را برای شرکت در این پیام دعوت کردیم." + success_group: "کاربر را برای شرکت در این گروه دعوت کردیم." error: "با معذرت٬ یک خطا برای دعوت آن کاربر وجود داشت" group_name: "نام گروه" controls: "مدیریت مبحث" @@ -1102,6 +1418,10 @@ fa_IR: error: "اینجا یک ایراد برای جابجایی نوشته ها به آن موضوع بود." instructions: other: "لطفاً موضوعی را که قصد دارید تا {{count}}از نوشته‌ها را به آن انتقال دهید، انتخاب نمایید." + merge_posts: + title: "ادغام نوشته‌های انتخاب شده" + action: "ادغام نوشته‌های انتخاب شده" + error: "خطایی در ادغام نوشته‌های انتخاب شده رخ داده است." change_owner: title: "تغییر مالکیت نوشته ها" action: "تغییر مالکیت" @@ -1130,11 +1450,14 @@ fa_IR: post: reply: " {{replyAvatar}} {{usernameLink}}" reply_topic: " {{link}}" + quote_reply: "نقل قول" edit: "در حال ویرایش {{link}} {{replyAvatar}} {{username}}" edit_reason: "دلیل:" post_number: "نوشته {{number}}" + wiki_last_edited_on: "دانشنامه آخرین بار در این زمان ویرایش شده" last_edited_on: "آخرین ویرایش نوشته در" reply_as_new_topic: "پاسخگویی به عنوان یک موضوع لینک شده" + reply_as_new_private_message: "پاسخ به عنوان پیام جدید به همین دریافت کنندگان" continue_discussion: "دنبالهٔ موضوع {{postLink}}:" follow_quote: "برو به نوشته ای که نقل‌قول شده" show_full: "نمایش کامل نوشته" @@ -1167,9 +1490,13 @@ fa_IR: no_value: "خیر، نگه دار" yes_value: "بله، رها کن" via_email: "این نوشته از طریق ایمیل ارسال شده است" + via_auto_generated_email: "این نوشته با ایمیل خودکار ارسال شده" whisper: "این ارسال نجوای خصوصی برای مدیران است" + wiki: + about: "این نوشته یک دانشنامه است" archetypes: save: ' ذخیره تنظیمات' + few_likes_left: "با تشکر از علاقه شما! فقط امکان چند پسندیدن دیگر را برای امروز دارید." controls: reply: "آغاز ساخت یک پاسخ به این نوشته" like: "شبیه این نوشته" @@ -1206,6 +1533,15 @@ fa_IR: bookmark: "برداشتن نشانک" like: "خنثی سازی لایک" vote: "خنثی سازی امتیاز" + people: + off_topic: "به عنوان خارج از موضوع علامت گذاری شده" + spam: "نشانه گذاری به عنوان هرزنامه" + inappropriate: "نشانه گذاری به عنوان نامناسب" + notify_moderators: "مدیر‌های مطلع شده" + notify_user: "یک پیام ارسال شد" + bookmark: "نشانه گذاری کن" + like: "پسندیده شد" + vote: "رای دهید" by_you: off_topic: "شما برای این مورد پرچم آف-تاپیک زدید" spam: "شما برای این مورد پرچم هرزنامه زدید" @@ -1252,6 +1588,9 @@ fa_IR: delete: confirm: other: "آیا مطمئنید که می‌خواهید همهٔ آن نوشته ها را پاک کنید؟" + merge: + confirm: + other: "آیا از ادغام {{count}} نوشته اطمینان دارید؟" revisions: controls: first: "بازبینی نخست" @@ -1260,14 +1599,31 @@ fa_IR: last: "بازبینی نهایی" hide: "مخفی کردن نسخه" show: "نمایش نسخه" + revert: "بازگشت به این بازبینی" + edit_wiki: "ویرایش دانشنامه" + edit_post: "ویرایش نوشته" comparing_previous_to_current_out_of_total: "{{previous}} {{current}} / {{total}}" displays: inline: title: "نمایش خروجی رندر با اضافات و از بین بردن درون خطی" + button: 'HTML' side_by_side: title: "نمایش تفاوت های خروجی رندر شده سو به سو" + button: 'HTML' side_by_side_markdown: title: "نمایش تفاوت های خروجی منبع اولیه سو به سو" + button: 'متن' + raw_email: + displays: + raw: + title: "نمایش ایمیل به صورت متن" + button: 'متن' + text_part: + title: "نمایش قسمت متنی ایمیل" + button: 'متن' + html_part: + title: "نمایش قسمت HTML ایمیل" + button: 'HTML' category: can: 'can… ' none: '(بدون دسته)' @@ -1279,6 +1635,10 @@ fa_IR: general: 'عمومی' settings: 'تنظیمات' topic_template: "قالب موضوع" + tags: "برچسب‌ها" + tags_allowed_tags: "برچسب‌هایی که فقط در این دسته‌بندی استفاده می‌شوند:" + tags_allowed_tag_groups: "گروه‌های برچسب که فقط در این دسته‌بندی استفاده می‌شوند" + tags_placeholder: "(اختیاری) فهرست برچسب‌های قابل استفاده" delete: 'پاک کردن دسته' create: 'دسته بندی جدید' create_long: 'ساختن یک دسته بندی جدید' @@ -1333,11 +1693,29 @@ fa_IR: muted: title: "بی صدا شد" description: "شما هیچ وقت از مبحث های تازه در این دسته بندی ها مطلع نمیشوید, و آن ها در آخرین ها نمایش داده نمیشوند." + sort_options: + default: "پیش‌فرض" + likes: "پسند‌ها" + op_likes: "پسند‌های نوشته اصلی" + views: "بازدید" + posts: "نوشته‌ها" + activity: "فعالیت" + posters: "ارسال کنندگان" + category: "دسته‌بندی" + created: "ایجاد شده" + sort_ascending: 'صعودی' + sort_descending: 'نزولی' + subcategory_list_styles: + rows: "سطر‌ها" + rows_with_featured_topics: "سطر‌ها و موضوعات برجسته" + boxes: "جعبه‌ها" + boxes_with_featured_topics: "جعبه‌ها با موضوعات برجسته" flagging: title: 'تشکر برای کمک به نگه داشتن جامعه ما بصورت مدنی !' action: 'پرچم‌گذاری نوشته' take_action: "اقدام" notify_action: 'پیام' + official_warning: 'اخطار رسمی' delete_spammer: "پاک کردن هرزنگار" yes_delete_spammer: "بله، پاک‌کردن هرزنگار" ip_address_missing: "(N/A)" @@ -1345,12 +1723,20 @@ fa_IR: submit_tooltip: "ایجاد پرچم خصوصی" take_action_tooltip: "رسیدن سریع به آستانه پرچم، بلافاصله به جای انتظار برای پرچم انجمن" cant: "متأسفیم، در این زمان نمی‌توانید روی این نوشته پرچم بگذارید." + notify_staff: 'کارمندان را به صورت خصوصی مطلع کن' formatted_name: off_topic: "آن موضوع قدیمی است" inappropriate: "این نامناسب است" spam: "آن هرزنامه است" custom_placeholder_notify_user: "خاص، سودمند باشید و همیشه مهربان." custom_placeholder_notify_moderators: "به ما اجازه دهید بدانیم شما در مورد چه چیز آن نگران هستید، و ارائه لینک مربوطه و نمونه آن امکان پذیر است." + custom_message: + at_least: + other: "حداقل {{count}} نویسه وارد کنید" + more: + other: "{{count}} نیاز است..." + left: + other: "{{count}} باقی مانده" flagging_topic: title: "تشکر برای کمک به جامعه مدنی انجمن ما!" action: "پرچم‌گذاری موضوع" @@ -1359,8 +1745,13 @@ fa_IR: title: "چکیدهٔ موضوع" participants_title: "نویسنده‌های فعال" links_title: "لینک‌های محبوب" + links_shown: "نمایش لینک‌های بیشتر..." clicks: other: "%{count} کلیک ها" + post_links: + about: "توسعه لینک‌های بیشتر برای این نوشته" + title: + other: "%{count} بیشتر" topic_statuses: warning: help: "این یک هشدار رسمی است." @@ -1396,6 +1787,8 @@ fa_IR: views_lowercase: other: "بازدیدها" replies: "پاسخ‌ها" + views_long: + other: "این موضوع {{number}} دیده شده" activity: "فعالیت" likes: "پسندها" likes_lowercase: @@ -1408,6 +1801,7 @@ fa_IR: history: "تاریخچه" changed_by: "توسط {{author}}" raw_email: + title: "ایمیل دریافتی" not_available: "در دسترس نیست!" categories_list: "فهرست دسته‌ بندی ها" filters: @@ -1484,6 +1878,13 @@ fa_IR: full: "ساختن / پاسخ دادن / دیدن" create_post: "پاسخ دادن / دیدن" readonly: "دیدن" + lightbox: + download: "دانلود" + keyboard_shortcuts_help: + title: 'میانبر‌های صفحه‌کلید' + jump_to: + title: 'برو به' + home: 'g, h خانه' badges: earned_n_times: other: "این مدال را %{count} بار به دست آورده" @@ -1492,6 +1893,93 @@ fa_IR: badge_count: other: "%{count} مدال" select_badge_for_title: انتخاب یک مدال برای استفاده در عنوان خود + badge_grouping: + community: + name: انجمن + trust_level: + name: سطح اعتماد + other: + name: دیگر + posting: + name: در حال نوشتن + tagging: + all_tags: "تمام برچسب‌ها" + selector_all_tags: "تمام برچسب‌ها" + selector_no_tags: "بدون برچسب" + changed: "برچسب‌های تغییر یافته:" + tags: "برچسب‌ها" + choose_for_topic: "انتخاب برچسب‌های اختیاری برای موضوع" + delete_tag: "حذف برچسب" + delete_confirm: "آیا از حذف برچسب اطمینان دارید؟" + rename_tag: "تغییر نام برچسب" + rename_instructions: "انتخاب نام جدید برای برچسب:" + sort_by: "مرتب سازی بر اساس:" + sort_by_count: "تعداد" + sort_by_name: "نام" + manage_groups: "مدیریت گروه‌های برچسب" + manage_groups_description: "تعریف گروه برای سازماندهی برچسب‌ها" + filters: + without_category: "%{filter} %{tag} موضوعات" + with_category: "%{filter} %{tag} موضوعات در %{category}" + untagged_without_category: "%{filter} موضوعات بدون برچسب" + untagged_with_category: "%{filter} موضوعات بدون برچسب در %{category}" + notifications: + watching: + title: "در حال مشاهده" + watching_first_post: + title: "در حال مشاهده اولین نوشته" + description: "شما برای اولین پست هر موضوع جدید در این برچسب مطلع خواهید شد." + tracking: + title: "پیگیری" + description: "به صورت خودکار در تمامی موضوعات این برچسب را پیگیری می‌کنید. تعداد نوشته‌های خوانده نشده کنار موضوع نمایش داده می‌شود." + regular: + title: "عادی" + description: "اگر کسی به صورت @نام به شما اشاره کند، به شما اطلاع داده خواهد شد." + muted: + title: "بی‌صدا شد" + description: "از موضوعات جدید در این برچسب مطلع نخواهید شد و در تب مطالب خوانده نشده‌ها نیز نمایش داده نمی‌شوند." + groups: + title: "برچسب گروه‌ها" + about: "برای مدیریت راحت‌تر گروه‌ها به آن‌ها برچسب اضافه کنید." + new: "گروه جدید" + tags_label: "برچسب‌های گروه:" + parent_tag_label: "برچسب مادر:" + parent_tag_placeholder: "اختیاری" + parent_tag_description: "برچسب‌های این گروه تا زمانی که برچسب مادر وجود نداشته باشد، قابل استفاده نیستند." + one_per_topic_label: "محدودیت یک برچسب به ازای هر موضوع در این گروه" + new_name: "گروه برچسب جدید" + save: "ذخیره" + delete: "حذف" + confirm_delete: "آیا از حذف گروه برچسب‌ها اطمینان دارید؟" + topics: + none: + unread: "شما موضوع خوانده نشده‌ای ندارید." + new: "شما موضوع جدیدی ندارید." + read: "شما هیچ موضوعی را نخوانده‌اید." + posted: "شما هیچ پستی در موضوعات ندارید." + latest: "موضوع اخیری وجود ندارد." + hot: "موضوع داغی وجود ندارد." + bookmarks: "موضوع نشانه گذاری شده‌ای ندارید." + top: "موضوع برتری وجود ندارد." + search: "نتیجه جستجوی دیگری وجود ندارد." + bottom: + latest: "موضوع تازه دیگری وجود ندارد." + hot: "موضوع داغ دیگری وجود ندارد" + posted: "موضوع نوشته شده دیگری وجود ندارد" + read: "موضوع خوانده شده دیگری وجود ندارد." + new: "موضوع جدید دیگری وجود ندارد." + unread: "موضوع خوانده نشده دیگری وجود ندارد." + top: "موضوع دیگری وجود ندارد." + bookmarks: "موضوع نشانه‌گذاری شده دیگری وجود ندارد." + search: "نتیجه جستجوی دیگری وجود ندارد" + invite: + custom_message: "دعوت خود را کمی بیشتر شخصی کنید با نوشتن یک " + custom_message_link: "پیام سف ارشی" + custom_message_placeholder: "متن پیام خود را وارد کنید" + custom_message_template_forum: "سلام، باید عضو این انجمن بشی!" + custom_message_template_topic: "سلام، فکر کردم از این موضوع لذت می‌بری!" + safe_mode: + enabled: "حالت امن فعال شده، برای خروج از حالت امن این پنجره مرورگر را ببندید." admin_js: type_to_filter: "بنویسید تا فیلتر کنید..." admin: @@ -1526,6 +2014,8 @@ fa_IR: backups: "پشتیبان ها" traffic_short: "ترافیک" traffic: "درخواست های نرم افزار وب" + page_views: "بازدید" + page_views_short: "بازدید" show_traffic_report: "نمایش دقیق گزارش ترافیک" reports: today: "امروز" @@ -1537,9 +2027,11 @@ fa_IR: 30_days_ago: "30 روز پیش" all: "همه" view_table: "جدول" + view_graph: "نمودار" refresh_report: "تازه کردن گزارش" start_date: "تاریخ شروع" end_date: "تاریخ پایان" + groups: "همه گروه‌ها" commits: latest_changes: "آخرین تغییرات: لطفا دوباره به روز رسانی کنید!" by: "توسط" @@ -1630,6 +2122,8 @@ fa_IR: add_owners: افزودن مالک incoming_email: "آدرس ایمیل های دریافتی سفارشی" incoming_email_placeholder: "آدرس ایمیل را وارد کنید" + none_selected: "برای شروع یک گروه انتخاب کنید" + no_custom_groups: "ایجاد گروه سفارشی" api: generate_master: "ایجاد کلید اصلی API" none: "هم اکنون هیچ کلید API فعالی وجود ندارد" @@ -1645,17 +2139,72 @@ fa_IR: all_users: "همه کاربران" note_html: "این کلید را امن نگهدارید، تمام کاربرانی که آن را دارند می توانند نوشته های دلخواه بسازند به عنوان هر کاربری" web_hooks: + title: "webhook ها" + none: "در حال حاضر webhook‌ی وجود ندارد" + instruction: "webhook ها به Discourse اجازه می‌دهند که به در زمان رخ دادن رویداد‌ها به سرویس‌های خارجی اعلام کنند. وقتی webhook فعال شود یک درخواست POST به لینک‌های ارائه شده ارسال می‌شود." + detailed_instruction: "وقتی رویداد اتفاق بیفتد، یک درخواست POST به لینک ارائه شده ارسال می‌شود." + new: "webhook جدید" + create: "ایجاد" + save: "ذخیره" + destroy: "حذف" + description: "توضیحات" + controls: "کنترل‌ها" + go_back: "بازگشت به فهرست" + payload_url: "لینک payload" + payload_url_placeholder: "https://example.com/postreceive" + warn_local_payload_url: "به نظر می‌رسد در حال تنظیم webhook در localhost هستید. این کار ممکن است باعث ایجاد خطا‌های غیر منتظره شود. ادامه می‌دهید؟" + secret_invalid: "رمز نباید شامل کاراکتر فاصله باشد." + secret_too_short: "رمز باید حداقل 12 کاراکتر باشد." + secret_placeholder: "یک رشته اختیاری که برای ایجاد امضا استفاده می‌شود." + event_type_missing: "باید حداقل یک نوع رویداد تنظیم کنید." + content_type: "نوع محتوا" + secret: "رمز" + event_chooser: "می‌خواهید با کدام رویداد این وب‌هوک را فعال کنید؟" + wildcard_event: "به من هر‌چیزی ارسال کن." + individual_event: "رویداد‌های جداگانه انتخاب کنید." + verify_certificate: "گواهی TLS را در لینک payload بررسی کنید." active: "فعال" + active_notice: "جزئیات رویداد را وقتی اتفاق می‌افتد ارائه می‌کنیم." + categories_filter_instructions: "webhook مرتبط فقط در صورتی فعال می‌شود که رویداد به دسته‌بندی‌های مشخص مرتبط باشد. برای فعال‌سازی webhook در تمام دسته‌بندی‌ها این قسمت را خالی بگذارید." + categories_filter: "دسته‌بندی‌های عامل" + groups_filter_instructions: "webhook مرتبط فقط در صورتی فعال می‌شود که رویداد به گروه‌های مشخص مرتبط باشد. برای فعال‌سازی webhook در تمام گروه‌ها این قسمت را خالی بگذارید." + groups_filter: "گروه‌های عامل" + delete_confirm: "حذف وب هوک؟" + topic_event: + name: "رویدادوو" + details: "وقتی یک موضوع جدید اصلاح، عوض یا حذف می‌شود." + post_event: + name: "رویداد نوشته" + details: "وقتی یک پاسخ، ویرایش، حذف یا بازگردانده شده." + user_event: + name: "رویداد کاربر" + details: "وقتی کاربر ایجاد تایید یا به‌روز می‌شود." delivery_status: + title: "وضعیت تحل" + inactive: "غیر‌فعال" failed: "ناموفق" successful: "موفق" events: + none: "رویداد مشابهی وجود ندارد" + redeliver: "تحویل مجدد" + incoming: + other: "{{count}} رویداد جدید جود دارد." + completed_in: + other: "در {{count}} ثانیه کامل شد." request: "درخواست" response: "پاسخ" + redeliver_confirm: "آیا از تحویل مجدد payload اطمینان دارید؟" + headers: "سربرگ‌ها" + payload: "Payload" body: "بدنه" go_list: "برو به لیست" + go_details: "ویرایش وب هوک" + go_events: "برو به رویداد‌ها" + ping: "پینگ" + status: "کد وضعیت" event_id: "آی دی" timestamp: "ساخته شده" + completion: "زمان تکمیل" actions: "کار ها" plugins: title: "افزونه ها" @@ -1675,6 +2224,14 @@ fa_IR: backups: "پشتیبان ها" logs: "گزارش ها" none: "هیچ پشتیبانی در دسترس نیست." + read_only: + enable: + title: "فعال‌سازی حالت فقط خواندنی" + label: "فعال‌سازی حالت فقط خواندنی" + confirm: "آیا از فعال کردن حالت فقط خواندنی اطمینان دارید؟" + disable: + title: "غیر‌فعال‌سازی حالت فقط خواندنی" + label: "غیر‌فعال‌سازی حالت فقط خواندنی" logs: none: "هنوز بدون گزارش است ..." columns: @@ -1700,6 +2257,8 @@ fa_IR: without_uploads: "بله (فایل ها را شامل نمی شود)" download: label: "دانلود" + title: "ارسال ایمیل با لینک دانلود" + alert: "لینک دانلود نسخه پشتیبان به ایمیل شما ارسال شد." destroy: title: "پاک کردن پشتیبان" confirm: "آیا مطمئنید که می‌خواهید پشتیبان را از بین ببرید؟" @@ -1711,6 +2270,7 @@ fa_IR: rollback: label: "عقبگرد" title: "عقب گرد پایگاه داده به حالت کار قبلی" + confirm: "آیا از بازنشانی پایگاه داده به حالت قبلی که کار می‌کرد، اطمینان دارید؟" export_csv: success: "فرایند برون ریزی، به شما از طریق پیام اطلاع رسانی خواهد شد وقتی این فرایند تکمیل شود." failed: "برون ریزی شکست خورد. لطفا لوگ گزارشات را مشاهده فرمایید." @@ -1729,36 +2289,22 @@ fa_IR: customize: title: "شخصی‌سازی" long_title: "شخصی‌سازی سایت" - css: "CSS" - header: "سردر" - top: "بالا" - footer: "پانوشته " - embedded_css: "CSS جاساز شده" - head_tag: - text: "" - title: "HTML هایی که قرار داده شده قبل از تگ " - body_tag: - text: "" - title: "HTML هایی که قرار داده شده قبل از تگ " - override_default: "شامل شیوه نامه استاندارد نکن" - enabled: "فعال شد؟" preview: "پیش‌نمایش" - undo_preview: "حذف پیش نمایش" - rescue_preview: "به سبک پیش فرض" - explain_preview: "مشاهده‌ی سایت با این قالب سفارشی" - explain_undo_preview: "بازگشت به شیوه نامه های فعال شخصی" - explain_rescue_preview: "دیدن سایت با شیوه نامه پیش فرض" + explain_preview: "نمایش سایت با این قالب" save: "ذخیره سازی" new: "تازه" new_style: "سبک جدید" import: "ورود داده‌ها" - import_title: "فایلی را انتخاب یا متنی را پیست کنید" delete: "پاک کردن" - delete_confirm: "پاک کردن این شخصی‌سازی؟" + delete_confirm: "حذف قالب" about: "اطلاح شیوه نامه CSS و هدر HTML در سایت، اضافه کردنیک سفارشی سازی برای شروع." color: "رنگ" opacity: "تاری" copy: "کپی" + copy_to_clipboard: "کپی در clipboard" + copied_to_clipboard: "کپی شد" + copy_to_clipboard_error: "خطا در کپی اطلاعات" + theme_owner: "قابل ویرایش نیست، مالک:" email_templates: title: "قالب های ایمیل" subject: "عنوان" @@ -1767,13 +2313,68 @@ fa_IR: none_selected: "یک قالب ایمیل برای شروع ویرایش انتخاب کنید." revert: "باطل کردن تغییرات" revert_confirm: "آیا مطمئن هستید که میخواهید تنظیمات را باطل کنید؟" - css_html: - title: "CSS/HTML" - long_title: "شخصی‌سازی CSS و HTML" + theme: + import_theme: "درون‌ریزی قالب" + customize_desc: "شخصی‌سازی:" + title: "قالب‌ها" + long_title: "اصلاح رنگ، CSS و HTML محتوای سایت." + edit: "ویرایش" + edit_confirm: "این قالب راه‌دور است، در صورت ویرایش HTML/CSS تغییرات شما بعد از به‌روز‌رسانی از بین خواهد رفت." + common: "مشترک" + desktop: "کامپیوتر" + mobile: "موبایل" + preview: "پیش‌نمایش" + is_default: "قالب پیش‌فرض" + user_selectable: "قالب توسط کاربر قابل تغییر است" + color_scheme: "طرح رنگ" + color_scheme_select: "انتخاب رنگ مورد استفاده قالب" + custom_sections: "بخش‌های سفارشی" + theme_components: "اجزاء قالب" + child_themes_check: "این قالب دارای قالب‌های فرزند است" + css_html: "CSS/HTML سفارشی" + edit_css_html: "ویرایش CSS/HTML" + edit_css_html_help: "شما CSS یا HTML را ویرایش نکرده‌اید" + import_web_tip: "مخزن شامل قالب است" + import_file_tip: "فایل .dcstyle.json شامل قالب است" + about_theme: "درباره قالب" + license: "مجوز" + component_of: "قالب جزئی است از:" + update_to_latest: "به‌روز‌رسانی به آخرین نسخه" + check_for_updates: "برررسی به‌روز‌رسانی‌ها" + updating: "در حال به‌روز‌رسانی" + up_to_date: "قالب به روز است،‌ آخرین بررسی در:" + add: "افزودن" + commits_behind: + other: "قالب {{count}} تغییر دیگر نیاز دارد." + scss: + text: "سی اس اس" + title: "کد CSS مد نظرتان را وارد کنید، از تمام کد‌های CSS و SCSS معتبر پشتیبانی می‌شود." + header: + text: "سربرگ" + title: "کد HTML را جهت نمایش بالای سربرگ سایت وارد کنید." + after_header: + text: "بعد از سربرگ" + title: "کد HTML را جهت نمایش در سربرگ تمامی صفحات سایت وارد کنید" + footer: + text: "پاورقی" + title: "کد HTML جهت نمایش در پاورقی سایت را وارد کنید" + embedded_scss: + text: "کد CSS جاسازی شده" + title: "کد CSS سفارشی مد نظرتان را اینجا وارد کنید" + head_tag: + text: "" + title: "کد HTML که قبل از تگ اضافه می‌شود" + body_tag: + text: "" + title: "کد HTML که قبل از تگ اضافه می‌شود" colors: + select_base: + title: "انتخاب طرح رنگ پایه:" + description: "طرح پایه:" title: "رنگ‌ها" + edit: "ویرایش طرح‌ رنگ" long_title: "طرح‌های رنگی" - about: "تغییر رنگ استفاده شده در انجمن بدون نوشتن کد CSS.با اضافه کردن یک طرح شروع کنید." + about: "رنگ‌های استفاده شده در قالب را تغییر دهید یا طرح رنگی جدید ایجاد کنید." new_name: "طرح رنگ جدید" copy_name_prefix: "نمونه سازی از" delete_confirm: "این طرح رنگ پاک شود؟" @@ -1821,6 +2422,7 @@ fa_IR: test_error: "در ارسال ایمیل آزمایشی مشکلی وجود داشته است. لطفاً مجدداً تنظیمات ایمیل خود را بررسی کنید، از این که هاستتان اتصالات ایمیل را مسدود نکرده اطمینان حاصل کرده و مجدداً تلاش کنید." sent: "فرستاده شده" skipped: "رد داده شده" + bounced: "برگشته" received: "دریافت شده" rejected: "رد شده" sent_at: "ارسال شده در" @@ -1834,14 +2436,36 @@ fa_IR: delivery_method: "روش تحویل" preview_digest_desc: "پیش نمایش محتوای خلاصه ایمیل های ارسال شده به کاربران غیر فعال." refresh: "تازه‌سازی" + send_digest_label: "ارسال نتیجه به:" + send_digest: "ارسال" + sending_email: "در حال ارسال ایمیل..." format: "قالب" html: "html" text: "متن" last_seen_user: "آخرین مشاهده کاربر :" + no_result: "نتیجه‌ای برای خلاصه پیدا نشد." reply_key: "کلید پاسخ" skipped_reason: "رد دادن دلیل" incoming_emails: from_address: "از طرف" + to_addresses: "به" + cc_addresses: "سایر گیرندگان" + subject: "موضوع" + error: "خطا" + none: "ایمیل ورودی پیدا نشد." + modal: + title: "جزئیات ایمیل ورودی" + error: "خطا" + headers: "سربرگ" + subject: "موضوع" + body: "متن" + rejection_message: "رد کردن ایمیل" + filters: + from_placeholder: "from@example.com" + to_placeholder: "to@example.com" + cc_placeholder: "cc@example.com" + subject_placeholder: "موضوع..." + error_placeholder: "خطا" logs: none: "هیچ آماری یافت نشد." filters: @@ -1889,8 +2513,8 @@ fa_IR: change_trust_level: "تغییر دادن سطح اعتماد" change_username: "تغییر نام کاربری" change_site_setting: "تغییر تنظیمات سایت" - change_site_customization: "تغییر سفارشی‌سازی سایت" - delete_site_customization: "پاک‌کردن سفارشی‌سازی سایت" + change_theme: "تغییر قالب" + delete_theme: "حذف قالب" change_site_text: "تغییر نوشته سایت" suspend_user: "کاربر تعلیق شده" unsuspend_user: "کابر تعلیق نشده" @@ -1905,6 +2529,23 @@ fa_IR: change_category_settings: "تغییر تنظیمات دسته بندی" delete_category: "حذف دسته بندی" create_category: "ساخت دسته بندی" + block_user: "بستن کاربر" + unblock_user: "باز کردن کاربر" + grant_admin: "اعطای مدیر کل" + revoke_admin: "لغو مدیر کل" + grant_moderation: "اعطای مدیریت" + revoke_moderation: "لغو مدیریت" + backup_create: "ایجاد نسخه پشتیبان" + deleted_tag: "برچسب حذف شده" + renamed_tag: "برچسب تغییر نام داده شده" + revoke_email: "لغو ایمیل" + lock_trust_level: "بستن سطح اعتماد" + unlock_trust_level: "باز کردن سطح اعتماد" + activate_user: "فعالسازی کاربر" + deactivate_user: "غیر فعالسازی کاربر" + change_readonly_mode: "تغییر حالت فقط خواندنی" + backup_download: "دانلود نسخه پشتیبان" + backup_destroy: "حذف نسخه پشتیبان" screened_emails: title: "ایمیل ها نمایش داده شده" description: "وقتی کسی سعی می کند یک حساب جدید ایجاد کند، از آدرس ایمیل زیر بررسی و ثبت نام مسدود خواهد شد، و یا برخی از اقدام های دیگر انجام می شود." @@ -1995,24 +2636,26 @@ fa_IR: suspend_reason: "دلیل" suspended_by: "تعلیق شده توسط" delete_all_posts: "پاک کردن همهٔ نوشته‌ها" + delete_all_posts_confirm_MF: "شما در حال حذف {POSTS, plural, one {1 نوشته} other {# نوشته}} and {TOPICS, plural, one {1 نوشته} other {# موضوع}} هستید. ادامه می‌دهید؟" suspend: "تعلیق" unsuspend: "خارج کردن از تعلیق" suspended: "تعلیق شد ؟" moderator: "مدیر ؟ " admin: "مدیر؟" blocked: "مسدود شد ؟" + staged: "کاربر خودکار" show_admin_profile: "مدیر" - edit_title: "ویرایش سرنویس" - save_title: "ذخیره سازی سرنویس" refresh_browsers: "تازه کردن اجباری مرورگر" refresh_browsers_message: "ارسال پیام به تمام مشتریان! " show_public_profile: "نمایش نمایه عمومی" impersonate: 'جعل هویت کردن' + action_logs: "گزارش گار" ip_lookup: "IP Lookup" log_out: "خروج" logged_out: "کاربر از کل دستگاه ها خارج شد." revoke_admin: 'ابطال مدیریت' grant_admin: 'اعطای مدیریت' + grant_admin_confirm: "برای تایید مدیریت جدید ایمیلی ارسال کردیم. لطفا ایمیل را باز کرده و دستور‌العمل آن را دنبال کنید." revoke_moderation: 'پس گرفتن مدیریت' grant_moderation: 'اعطای مدیریت' unblock: 'رفع انسداد' @@ -2062,9 +2705,20 @@ fa_IR: deactivate_failed: "برای غیرفعال کردن این کاربر مشکلی وجود دارد." unblock_failed: ' برداشتن رفع انسداد این کاربر مشکلی وجود دارد.' block_failed: 'برای انسداد این کاربر مشکلی وجود دارد.' + block_confirm: 'آیا از بستن دسترسی کاربر اطمینان دارید؟ بعد از انجام این کار امکان ایجاد موضوع یا ارسال پست نخواهند داشت.' + block_accept: 'بله، این کاربر را مسدود کن' + bounce_score: "امتیاز ویژه" + reset_bounce_score: + label: "بازنشانی" + title: "بازنشانی امتیاز ویژه به 0" deactivate_explanation: "کاربر غیر فعال باید دوباره ایمیل خود را تایید کند." suspended_explanation: "کاربر تعلیق شده نمی‌تواند وارد سیستم شود." block_explanation: "کاربر انسداد شده نمی‌تواند نوشته ای بگذارد یا موضوعی آغاز کند." + staged_explanation: "کاربر خودکار فقط در بخش های خاص و با ایمیل می‌تواند پست ارسال کند." + bounce_score_explanation: + none: "بازتابی به تازگی از آن ایمیل دریافت نشده." + some: "تعدادی بازتاب به تازگی از آن ایمیل دریافت شدند." + threshold_reached: "بازتاب های زیادی از آن ایمیل دریافت شده." trust_level_change_failed: "در تغییر سطح اعتماد کاربر مشکلی پیش آمد." suspend_modal_title: "کاربر تعلیق شده" trust_level_2_users: "کاربران سطح اعتماد 2" @@ -2075,6 +2729,8 @@ fa_IR: unlock_trust_level: "باز کردن سطح اعتماد" tl3_requirements: title: "شرایط لازم برای سطح اعتماد 3." + table_title: + other: "در %{count} روز گذشته" value_heading: "مقدار" requirement_heading: "نیازمندی‌ها" visits: "بازدیدها" @@ -2130,6 +2786,10 @@ fa_IR: title: "در نمایه عمومی نمایش داده شود؟" enabled: "نمایش در نمایه" disabled: "در نمایه نشان ندهد" + show_on_user_card: + title: "در کارت کاربر نمایش داده شود؟" + enabled: "در کارت کاربر نمایش داده شده" + disabled: "در کارت کاربر نمایش داده نشده" field_types: text: 'فیلد متن' confirm: 'تاییدیه' @@ -2170,11 +2830,15 @@ fa_IR: developer: 'توسعه دهنده' embedding: "توکاری" legal: "حقوقی" + user_api: 'API کاربر' uncategorized: 'دیگر' backups: "پشتیبان‌ها" login: "ورود" plugins: "افزونه ها" user_preferences: "تنظیمات کاربری" + tags: "برچسب‌ها" + search: "جستجو" + groups: "گروه‌ها" badges: title: مدال ها new_badge: مدال جدید @@ -2183,6 +2847,7 @@ fa_IR: badge: مدال display_name: نام نمایشی description: توضیح + long_description: شرح کامل badge_type: نوع مدال badge_grouping: گروه badge_groupings: @@ -2222,6 +2887,7 @@ fa_IR: post_revision: "هنگامی که یک کاربر نوشته ای ویرایش می‌کند یا فرستد" trust_level_change: "هنگامی که کاربری سطح اعتماد را تغییر می‌دهد" user_change: "هنگامی که کاربری ویرایش یا ساخته می‌شود" + post_processed: "پس از پردازش یک پست" preview: link_text: "پیش نمایش مدال های اعطایی" plan_text: "پیشنمایش با طرح پرسش" @@ -2253,6 +2919,7 @@ fa_IR: sample: "از این کد HTML در سایت خود استفاده کنید تا بتوانید مبحث های Discourse ایجاد کنید یا جاساز کنید, این کد REPLACE_ME را به URL استاندارد صفحه ای که بر روی آن جاسازی میکنید تغییر دهید." title: "جاسازی" host: "میزبان های مجاز" + path_whitelist: "مسیر لیست سفید" edit: "ویرایش" category: "ارسال به دسته بندی" add_host: "اضافه کردن میزبان" @@ -2264,9 +2931,11 @@ fa_IR: embed_by_username: "نام کاربری برای ساخت مبحث" embed_post_limit: "حداکثر تعداد پست هایی که میتوان جاساز کرد" embed_username_key_from_feed: "کلیدی برای کشیدن نام کاربری Discourse از فید" + embed_title_scrubber: "عبارت منظم استفاده میشه برای ورز دادن عنوان پست" embed_truncate: "کوتاه کردن نوشته های جاسازی شده" embed_whitelist_selector: "CSS انتخاب کننده برای المان هایی که اجازه دارند جاسازی شوند" embed_blacklist_selector: "انتخاب کننده CSS برای المان هایی که از جاسازی پاک شده اند" + embed_classname_whitelist: " دسترسی به کلاس های سی اس اس" feed_polling_enabled: "وارد کردن پست ها توسط RSS/ATOM" feed_polling_url: " لینک RSS/ATOM فید برای خزیدن" save: "ذخیره تنظیمات کدهای جاساز" @@ -2285,3 +2954,21 @@ fa_IR: label: "جدید:" add: "افزودن" filter: "جستجو (آدرس یا آدرس خارجی)" + wizard_js: + wizard: + done: "انجام شد" + back: "بازگشت" + next: "بعدی" + step: "%{current} از %{total}" + upload: "بارگذاری" + uploading: "در حال بار گذاری ..." + quit: "شاید بعدا" + staff_count: + other: "انجمن شما دارای %{count} کارمند است." + invites: + add_user: "اضافه کردن" + none_added: "هیچ کدام از کارمندان را دعوت نکردید. ادامه می‌دهید؟" + roles: + admin: "مدیر" + moderator: "ناظم" + regular: "کاربر عادی" diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index 0518f2ac4c..c2f53d5d32 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -101,6 +101,7 @@ fi: other: "%{count} vuotta myöhemmin" previous_month: 'Edellinen kuukausi' next_month: 'Seuraava kuukausi' + placeholder: Valitse päivämäärä share: topic: 'jaa linkki tähän ketjuun' post: '%{postNumber}. viesti' @@ -143,6 +144,8 @@ fi: emails_are_disabled: "Ylläpitäjä on estänyt kaiken lähtevän sähköpostiliikenteen. Mitään sähköposti-ilmoituksia ei lähetetä." bootstrap_mode_enabled: "Jotta uuden palsta käynnistäminen olisi helpompaa, on sivusto asetettu aloitustilaan. Kaikki uudet käyttäjät siirretään automaattisesti luottamustasolle 1 ja heille lähetetään sähköpostitiivistelmät päivittäin. Tämä tila poistetaan automaattisesti, kun käyttäjien määrä ylittää %{min_users}." bootstrap_mode_disabled: "Aloitustila poistetaan seuraavan 24 tunnin aikana." + themes: + default_description: "Oletus" s3: regions: us_east_1: "itäinen USA (Pohjois-Virginia)" @@ -150,6 +153,7 @@ fi: us_west_2: "Läntinen USA (Oregon)" us_gov_west_1: "AWS GovCloud (USA)" eu_west_1: "EU (Irlanti)" + eu_west_2: "EU (Lontoo)" eu_central_1: "EU (Frankfurt)" ap_southeast_1: "Aasia ja Tyynimeri (Singapore)" ap_southeast_2: "Aasia ja Tyynimeri (Sydney)" @@ -386,6 +390,7 @@ fi: posts: "Viestit" mentions: "Maininnat" messages: "Viestit" + notification_level: "Ryhmäviestien oletusilmoitusasetus" alias_levels: title: "Ketkä voivat lähettää viestejä tälle ryhmälle tai @mainita sen?" nobody: "Ei kukaan" @@ -571,6 +576,7 @@ fi: revoke_access: "Peru käyttöoikeus" undo_revoke_access: "Peru käyttöoikeuden peruminen" api_approved: "Sallittu:" + theme: "Teema" staff_counters: flags_given: "hyödyllistä liputusta" flagged_posts: "liputettua viestiä" @@ -1082,15 +1088,6 @@ fi: title: "Unohditko lisätä vastaanottajia?" body: "Olet lähettämässä viestiä vain itsellesi!" admin_options_title: "Tämän ketjun vain henkilökunnalle näytettävät asetukset" - auto_close: - label: "Sulje ketju automaattisesti tämän ajan jälkeen:" - error: "Arvo ei kelpaa." - based_on_last_post: "Älä sulje ennen kuin viimeisin viesti ketjussa on vähintään näin vanha." - all: - examples: 'Syötä aika tunteina (24), absoluuttisena aikana (17:30) tai aikaleimana (2013-11-22 14:00).' - limited: - units: "(# tuntia)" - examples: 'Syötä aika tunteina (24).' notifications: title: "ilmoitukset @nimeen viittauksista, vastauksista omiin viesteihin ja ketjuihin, viesteistä ym." none: "Ilmoitusten lataaminen ei onnistunut." @@ -1342,15 +1339,18 @@ fi: topic_status_update: title: "Aseta ajastin ketjulle" save: "Aseta ajastin" - time: "Aika:" + num_of_hours: "Kuinka monta tuntia:" remove: "Poista ajastin" publish_to: "Mihin julkaistaan:" + when: "Milloin:" auto_update_input: - limited: - units: "(# tuntia)" - examples: 'Syötä aika tunteina (24).' - all: - examples: 'Syötä aika tunteina (24), kellonaikana (17:30) tai aikaleimana (2013-11-22 14:00).' + later_today: "Myöhemmin tänään" + tomorrow: "Huomenna" + later_this_week: "Myöhemmin tällä viikolla" + this_weekend: "Viikonloppuna" + next_week: "Ensi viikolla" + pick_date_and_time: "Valitse päivämäärä ja kellonaika" + set_based_on_last_post: "Sulje viimeisimmän viestin mukaan" publish_to_category: title: "Julkaise määrättynä ajankohtana" temp_open: @@ -1361,7 +1361,7 @@ fi: title: "Sulje väliaikaisesti" auto_close: title: "Sulje ketju automaattisesti" - label: "Aika, jolloin suljetaan automaattisesti:" + label: "Tunteja automaattiseen sulkeutumiseen:" error: "Arvo ei kelpaa." based_on_last_post: "Älä sulje ennen kuin viimeisin viesti ketjussa on ainakin näin vanha." status_update_notice: @@ -1399,10 +1399,10 @@ fi: '3_2': 'Saat ilmoituksia, koska olet asettanut ketjun tarkkailuun.' '3_1': 'Saat ilmoituksia, koska loit tämän ketjun.' '3': 'Saat ilmoituksia, koska olet asettanut ketjun tarkkailuun.' - '2_8': 'Saat ilmoituksia, koska olet asettanut tämän alueen seurantaan.' - '2_4': 'Saat ilmoituksia, koska olet kirjoittanut ketjuun.' - '2_2': 'Saat ilmoituksia, koska olet asettanut ketjun seurantaan.' - '2': 'Saat ilmoituksia, koska luit ketjua aiemmin.' + '2_8': 'Näet uusien vastausten määrän, koska olet asettanut tämän alueen seurantaan.' + '2_4': 'Näet uusien vastausten määrän, koska olet kirjoittanut ketjuun.' + '2_2': 'Näet uusien vastausten määrän, koska olet asettanut ketjun seurantaan.' + '2': 'Näet uusien vastausten määrän, koska luit ketjua aiemmin.' '1_2': 'Saat ilmoituksen jos joku mainitsee @nimesi tai vastaa sinulle.' '1': 'Saat ilmoituksen jos joku mainitsee @nimesi tai vastaa sinulle.' '0_7': 'Et saa mitään ilmoituksia tältä alueelta.' @@ -2555,36 +2555,22 @@ fi: customize: title: "Mukauta" long_title: "Sivuston mukautukset" - css: "CSS" - header: "Header" - top: "Alku" - footer: "Footer" - embedded_css: "Upotuksen CSS" - head_tag: - text: "" - title: "HTML, joka lisätään ennen elementtiä" - body_tag: - text: "" - title: "HTML, joka lisätään ennen elementtiä" - override_default: "Älä sisällytä oletus-tyylitiedostoa" - enabled: "Otettu käyttöön?" preview: "esikatselu" - undo_preview: "poista esikatselu" - rescue_preview: "oletustyyli" - explain_preview: "Esikatsele sivustoa käyttäen tätä tyylitiedostoa" - explain_undo_preview: "Siirry takaisin nykyisin käytössä olevaan tyylitiedostoon" - explain_rescue_preview: "Esikatsele sivustoa käyttäen oletustyylitiedostoa" + explain_preview: "Näe miltä sivusto näyttää tällä teemalla" save: "Tallenna" new: "Uusi" new_style: "Uusi tyyli" import: "Tuo" - import_title: "Valitse tiedosto tai liitä tekstiä" delete: "Poista" - delete_confirm: "Poista tämä mukautus?" + delete_confirm: "Poista tämä teema?" about: "Muokkaa sivuston CSS tyylitiedostoja ja HTML headeria. Lisää mukautus aloittaaksesi." color: "Väri" opacity: "Läpinäkyvyys" copy: "Kopioi" + copy_to_clipboard: "Kopioi leikepöydälle" + copied_to_clipboard: "Kopioitiin leikepöydälle" + copy_to_clipboard_error: "Virhe kopioimisessa leikepöydälle" + theme_owner: "Et voi muokata, omistaja:" email_templates: title: "Sähköpostipohjat" subject: "Otsikko" @@ -2593,13 +2579,50 @@ fi: none_selected: "Aloita muokkaaminen valitsemalla sähköpostiluonnos." revert: "Peru muutokset" revert_confirm: "Haluatko varmasti peruuttaa muutokset?" - css_html: - title: "CSS/HTML" - long_title: "CSS ja HTML Kustomoinnit" + theme: + import_theme: "Tuo teema" + customize_desc: "Mukauta:" + title: "Teemat" + long_title: "Muuta sivustosi värejä, CSS:ää ja HTML-sisältöä" + edit: "Muokkaa" + edit_confirm: "Tämä on ulkoinen teema. Jos muokkaat CSS:ää tai HTML:ää, muutokset kumoutuvat kun päivität teeman." + common: "Yhteiset" + desktop: "Työpöytä" + mobile: "Mobiili" + preview: "Esikatselu" + is_default: "Teema on käytössä oletuksena" + user_selectable: "Teema on tarjolla käyttäjille" + color_scheme: "Värimalli" + color_scheme_select: "Valitse värit, joita teema käyttä" + custom_sections: "Mukautetut osiot:" + theme_components: "Teeman osat" + child_themes_check: "Teemaan kuuluu tytärteemoja" + css_html: "Mukautettu CSS/HTML" + edit_css_html: "Muokkaa CSS/HTML" + edit_css_html_help: "Et ole muokannut CSS:ää tai HTML:ää" + import_web_tip: "Tietovarasto, jossa teema on" + import_file_tip: ".dcstyle.json-tiedosto, jossa teema on" + about_theme: "Tietoa teemasta" + license: "Lisenssi" + component_of: "Teema on osa teemaa:" + update_to_latest: "Päivitä tuoreimpaan" + check_for_updates: "Hae päivityksiä" + updating: "Päivitetään..." + up_to_date: "Teema on ajan tasalla, viimeksi tarkastettu:" + add: "Lisää" + commits_behind: + one: "Teema on yhden muutoksen perässä!" + other: "Teema on {{count}} muutosta perässä!" + scss: + text: "CSS" + title: "Lisää mukautettua CSS:ää, hyväksymme käyvät CSS- ja SCSS-tyylit" + header: + text: "Ylätunniste" colors: title: "Värit" + edit: "Muokkaa värimalleja" long_title: "Värimallit" - about: "Muokkaa sivuston värejä kirjoittamatta CSS-koodia. Lisää värimallia aloittaaksesi." + about: "Muuta teemojesi värejä. Aloita luomalla uusi värimalli." new_name: "Uusi värimalli" copy_name_prefix: "Kopio" delete_confirm: "Poista tämä värimalli?" @@ -2738,8 +2761,8 @@ fi: change_trust_level: "vaihda luottamustasoa" change_username: "vaihda käyttäjätunnus" change_site_setting: "muuta sivuston asetusta" - change_site_customization: "vaihda sivuston mukautusta" - delete_site_customization: "poista sivuston mukautus" + change_theme: "muuta teemaa" + delete_theme: "poista teema" change_site_text: "muutos sivuston teksteissä" suspend_user: "hyllytä käyttäjä" unsuspend_user: "poista hyllytys" @@ -2874,10 +2897,8 @@ fi: blocked: "Estetty?" staged: "Luotu?" show_admin_profile: "Ylläpito" - edit_title: "Muokkaa nimikettä" - save_title: "Tallenna nimike" refresh_browsers: "Pakota sivun uudelleen lataus" - refresh_browsers_message: "Viesti lähetetty kaikille asiakkaille!" + refresh_browsers_message: "Sanoma lähetettiin kaikille päätelaitteille!" show_public_profile: "Näytä julkinen profiili" impersonate: 'Esiinny käyttäjänä' action_logs: "Toimintaloki" diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index f0507ce6dd..e58152cf9c 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -135,7 +135,11 @@ fr: visible: enabled: 'a rendu ce sujet visible %{when}' disabled: 'a rendu ce sujet invisible %{when}' + banner: + enabled: 'a mis à la une %{when}. Il sera affiché en haut de chaque page jusqu’à ce qu''il soit ignoré par un utilisateur.' + disabled: 'a supprimé de la une %{when}. Il ne sera plus affiché en haut de chaque page.' topic_admin_menu: "actions d'administration sur le sujet" + wizard_required: "Bienvenue sur votre nouveau Discourse ! Démarrons pas l'assistant de configuration ✨" emails_are_disabled: "Le courriel sortant a été désactivé par un administrateur. Aucune notification courriel ne sera envoyée." bootstrap_mode_enabled: "Pour rendre le lancement de votre site plus facile, vous êtes en mode 'bootstrap'. Tout nouveau utilisateur sera accordé le niveau de confirance 1 et aura les résumés par courriel hebdomadaires activés. Ceci cessera d'être le cas lorsque le nombre d'utilisateurs dépassera %{min_users}." bootstrap_mode_disabled: "Le mode Bootstrap sera désactivé dans les prochaines 24 heures." @@ -362,7 +366,7 @@ fr: automatic_group: Groupe automatique closed_group: Groupe fermé is_group_user: "Vous êtes membre de ce groupe" - allow_membership_requests: "Autoriser les utilisateurs à envoyer des demandes d'adhésion aux propriétaires de groupe (nécessite que tout le monde puisse mentionner le groupe)" + allow_membership_requests: "Autoriser les utilisateurs à envoyer des demandes d'adhésion aux propriétaires de groupe" membership: "Adhésion" name: "Nom" user_count: "Nombre de membres" @@ -560,6 +564,7 @@ fr: muted_users_instructions: "Cacher toutes les notifications de ces utilisateurs." muted_topics_link: "Afficher les sujets en silencieux" watched_topics_link: "Afficher les sujets surveillés" + tracked_topics_link: "Afficher les sujets suivis" automatically_unpin_topics: "Désépingler automatiquement les sujets quand j'arrive à la fin." apps: "Applications" revoke_access: "Révoquer l'accès" @@ -927,6 +932,7 @@ fr: logging_in: "Connexion en cours…" or: "ou" authenticating: "Authentification…" + awaiting_activation: "Votre compte est en attente d'activation, utilisez le lien mot de passe oublié pour envoyer un autre courriel d'activation." awaiting_approval: "Votre compte n'a pas encore été approuvé par un modérateur. Vous recevrez une confirmation par courriel lors de l'activation." requires_invite: "Désolé, l'accès à ce forum est sur invitation seulement." not_activated: "Vous ne pouvez pas vous encore vous connecter. Nous avons envoyé un courriel d'activation à {{sentTo}}. Merci de suivre les instructions afin d'activer votre compte." @@ -935,6 +941,8 @@ fr: resend_activation_email: "Cliquez ici pour envoyer à nouveau le courriel d'activation." resend_title: "Renvoyer le courriel d'activation" change_email: "Changer l'adresse de courriel" + provide_new_email: "Donnez une nouvelle adresse et nous allons renvoyer votre courriel de confirmation." + submit_new_email: "Mettre à jour l'adresse de courriel" sent_activation_email_again: "Nous venons d'envoyer un nouveau courriel d'activation à {{currentEmail}}. Il peut prendre quelques minutes à arriver; n'oubliez pas de vérifier votre répertoire spam." to_continue: "Veuillez vous connecter" preferences: "Vous devez être connecté pour modifier vos préférences utilisateur." @@ -1073,15 +1081,6 @@ fr: title: "Avez-vous oublié d'ajouter des destinataires ?" body: "Pour le moment, ce message est uniquement envoyé à vous-même !" admin_options_title: "Paramètres optionnels pour ce sujet" - auto_close: - label: "Heure de fermeture automatique du sujet :" - error: "Merci d'entrer une valeur valide." - based_on_last_post: "Ne pas fermer tant que le dernier message du sujet n'a pas atteint cette ancienneté." - all: - examples: 'Saisir un nombre d''heures (24), une heure absolue (17:30) ou une date (2013-11-22 14:00).' - limited: - units: "(# d'heures)" - examples: 'Saisir le nombre d''heures (24).' notifications: title: "notifications des mentions de votre @pseudo, des réponses à vos messages, à vos sujets, etc." none: "Impossible de charger les notifications pour le moment." @@ -1151,6 +1150,7 @@ fr: sort_by: "Trier par" relevance: "Pertinence" latest_post: "Dernier message" + latest_topic: "Dernier sujet" most_viewed: "Plus vu" most_liked: "Plus aimé" select_all: "Sélectionner tout" @@ -1192,6 +1192,8 @@ fr: first: qui sont les premiers messages pinned: qui sont épinglés unpinned: qui ne sont pas épinglés + seen: j'ai lu + unseen: je n'ai pas lu wiki: qui sont des wikis statuses: label: Dont les sujets @@ -1327,11 +1329,37 @@ fr: jump_reply_up: aller à des réponses précédentes jump_reply_down: allez à des réponses ultérieures deleted: "Ce sujet a été supprimé" - auto_close_notice: "Ce sujet sera automatiquement fermé %{timeLeft}." - auto_close_notice_based_on_last_post: "Ce sujet sera fermé %{duration} après la dernière réponse." + topic_status_update: + title: "Planifier une action" + save: "Planifier" + time: "Heure :" + remove: "Supprimer la planification" + publish_to: "Publier dans :" + auto_update_input: + limited: + units: "(# d'heures)" + examples: 'Saisir le nombre d''heures (24).' + all: + examples: 'Entrez un nombre d''heures (24), une heure absolue (17:30) ou une date (2013-11-22 14:00).' + publish_to_category: + title: "Publier" + temp_open: + title: "Ouvrir temporairement" + auto_reopen: + title: "Ouvrir automatiquement le sujet" + temp_close: + title: "Fermer temporairement" + auto_close: + title: "Fermer automatiquement le sujet" + label: "Heure de fermeture automatique:" + error: "Veuillez entrer une valeur valide." + based_on_last_post: "Ne pas fermer tant que le dernier message du sujet n'a pas atteint cette ancienneté." + status_update_notice: + auto_open: "Ce sujet sera automatiquement ouvert %{timeLeft}." + auto_close: "Ce sujet sera automatiquement fermé %{timeLeft}." + auto_publish_to_category: "Ce sujet sera publié dans #%{categoryName} %{timeLeft}." + auto_close_based_on_last_post: "Ce sujet sera fermé %{duration} après la dernière réponse." auto_close_title: 'Paramètres de fermeture automatique' - auto_close_save: "Sauvegarder" - auto_close_remove: "Ne pas fermer automatiquement ce sujet" auto_close_immediate: one: "Le dernier message dans ce sujet est déjà vieux de 1 heure donc le sujet sera immédiatement fermé." other: "Le dernier message dans ce sujet est déjà vieux de %{count} heures donc le sujet sera immédiatement fermé." @@ -1364,7 +1392,7 @@ fr: '2_8': 'Vous recevrez des notifications parce que vous suivez cette catégorie.' '2_4': 'Vous recevrez des notifications car vous avez écrit une réponse dans ce sujet.' '2_2': 'Vous recevrez des notifications car vous suivez ce sujet.' - '2': 'Vous recevrez des notifications car vous avez lu ce sujet.' + '2': 'Vous recevrez des notifications car vous avez lu ce sujet.' '1_2': 'Vous serez notifié si quelqu''un mentionne votre @pseudo ou vous répond.' '1': 'Vous serez notifié si quelqu''un mentionne votre @pseudo ou vous répond.' '0_7': 'Vous ignorez toutes les notifications de cette catégorie.' @@ -1400,7 +1428,7 @@ fr: open: "Ouvrir le sujet" close: "Fermer le sujet" multi_select: "Sélectionner les messages…" - auto_close: "Fermeture automatique…" + timed_update: "Planifier une action…" pin: "Épingler le sujet…" unpin: "Désépingler le sujet…" unarchive: "Désarchiver le sujet" @@ -1732,6 +1760,17 @@ fr: side_by_side_markdown: title: "Afficher les différences de la source côte-à-côte" button: 'Brut' + raw_email: + displays: + raw: + title: "Afficher le courriel brut" + button: 'Brut' + text_part: + title: "Afficher le contenu texte du courriel" + button: 'Texte' + html_part: + title: "Afficher le contenu HTML du courriel" + button: 'HTML' category: can: 'peut… ' none: '(aucune catégorie)' @@ -1784,7 +1823,13 @@ fr: email_in_disabled_click: 'activer le paramètre "email in".' suppress_from_homepage: "Retirer cette catégorie de la page d'accueil" show_subcategory_list: "Afficher la liste des sous-catégories au dessus des sujets dans cette catégorie." + num_featured_topics: "Nombre de sujets affichés sur la page des catégories :" + subcategory_num_featured_topics: "Nombre de sujets sélectionnés sur la page des catégories parentes :" all_topics_wiki: "Faire des nouveaux sujets des Wikis par défaut" + subcategory_list_style: "Style des listes de sous-catégories :" + sort_order: "Trier la liste de sujets par :" + default_view: "Liste de sujets par défaut :" + default_top_period: "Période des meilleurs sujets par défaut :" allow_badges_label: "Autoriser les badges à être accordé dans cette catégorie" edit_permissions: "Modifier les permissions" add_permission: "Ajouter la permission" @@ -1822,6 +1867,11 @@ fr: created: "Création" sort_ascending: 'Croissant' sort_descending: 'Décroissant' + subcategory_list_styles: + rows: "Rangées" + rows_with_featured_topics: "Rangées avec sujets sélectionnés" + boxes: "Boîtes" + boxes_with_featured_topics: "Boîtes avec sujets sélectionnés" flagging: title: 'Merci d''aider à garder notre communauté civilisée !' action: 'Signaler ce message' @@ -1923,6 +1973,7 @@ fr: history: "Historique" changed_by: "par {{author}}" raw_email: + title: "Courriel entrant" not_available: "Indisponible !" categories_list: "Liste des catégories" filters: @@ -2325,6 +2376,8 @@ fr: add_owners: Ajouter des propriétaires incoming_email: "Adresse de courriel entrant personnalisée" incoming_email_placeholder: "Entrer une adresse de courriel" + none_selected: "Sélectionnez un groupe pour commencer" + no_custom_groups: "Créer un nouveau groupe personnalisé" api: generate_master: "Générer une clé d'API maître" none: "Il n'y a pas de clés API actives en ce moment." @@ -2460,6 +2513,8 @@ fr: without_uploads: "Oui (ne pas inclure les fichiers)" download: label: "Télécharger" + title: "Envoyer un courriel avec un lien de téléchargement" + alert: "Un lien pour télécharger la sauvegarder vous a été envoyé. " destroy: title: "Supprimer la sauvegarde" confirm: "Êtes-vous sûr de vouloir détruire cette sauvegarde?" @@ -2490,32 +2545,12 @@ fr: customize: title: "Personnaliser" long_title: "Personnalisations du site" - css: "CSS" - header: "Entête" - top: "Top" - footer: "Pied de page" - embedded_css: "CSS intégré" - head_tag: - text: "" - title: "HTML qui sera inséré avant la balise " - body_tag: - text: "" - title: "HTML qui sera inséré avant la balise " - override_default: "Ne pas inclure la feuille de style par défaut" - enabled: "Activé ?" preview: "prévisualiser" - undo_preview: "supprimer l'aperçu" - rescue_preview: "style par défaut" - explain_preview: "Voir le site avec la feuille de style personnalisé" - explain_undo_preview: "Revenir à la feuille de style personnalisé actuellement activée" - explain_rescue_preview: "Voir le site avec la feuille de style par défaut" save: "Sauvegarder" new: "Nouveau" new_style: "Nouveau style" import: "Importer" - import_title: "Sélectionnez un fichier ou collez du texte" delete: "Supprimer" - delete_confirm: "Supprimer cette personnalisation" about: "Modification des feuilles de styles et en-têtes de votre site. Ajouter un style personnalisé pour commencer." color: "Couleur" opacity: "Opacité" @@ -2528,13 +2563,9 @@ fr: none_selected: "Choisissez un modèle de courriel pour commencer l'édition." revert: "Annuler les changements" revert_confirm: "Êtes-vous sûr de vouloir annuler vos changements ?" - css_html: - title: "CSS/HTML" - long_title: "Personnalisations du CSS et HTML" colors: title: "Couleurs" long_title: "Palettes de couleurs" - about: "Modification des couleurs utilisés par le site sans écrire du CSS. Ajouter une palette pour commencer." new_name: "Nouvelle palette de couleurs" copy_name_prefix: "Copie de" delete_confirm: "Supprimer cette palette de couleurs ?" @@ -2673,8 +2704,6 @@ fr: change_trust_level: "modifier le niveau de confiance" change_username: "modifier pseudo" change_site_setting: "modifier les paramètres du site" - change_site_customization: "modifier la personnalisation du site" - delete_site_customization: "supprimer la personnalisation du site" change_site_text: "modifier le texte du site" suspend_user: "suspendre l'utilisateur" unsuspend_user: "annuler la suspension de l'utilisateur" @@ -2809,8 +2838,6 @@ fr: blocked: "Bloqué ?" staged: "En attente ?" show_admin_profile: "Administration" - edit_title: "Modifier le titre" - save_title: "Sauvegarder le titre" refresh_browsers: "Forcer le rafraîchissement du navigateur" refresh_browsers_message: "Message envoyé à tous les clients !" show_public_profile: "Afficher le profil public" @@ -2821,6 +2848,7 @@ fr: logged_out: "L'utilisateur s'est déconnecté de tous les appareils" revoke_admin: 'Révoquer les droits d''administration' grant_admin: 'Accorder les droits d''administration' + grant_admin_confirm: "Nous vous avons envoyé un courriel pour vérifier le nouvel administrateur. Veuillez le lire et suivre les instructions." revoke_moderation: 'Révoquer les droits de modération' grant_moderation: 'Accorder les droits de modération' unblock: 'Débloquer' diff --git a/config/locales/client.gl.yml b/config/locales/client.gl.yml index ec9f0a72b4..7ca9300598 100644 --- a/config/locales/client.gl.yml +++ b/config/locales/client.gl.yml @@ -860,15 +860,6 @@ gl: modal_cancel: "Cancelar" cant_send_pm: "Sentímolo pero non podes enviar unha mensaxe a %{username}." admin_options_title: "Axustes do equipo para este tema" - auto_close: - label: "Tempo para o peche automático deste tema:" - error: "Introduce un valor correcto." - based_on_last_post: "Non pechar até que a última publicación do tema teña alomenos este tempo." - all: - examples: 'Introducir o número de horas (24), hora absoluta (17:30) ou a marca data/hora (2013-11-22 14:00).' - limited: - units: "(# de horas)" - examples: 'Introducir o número de horas (24).' notifications: title: "notificacións das mencións ao teu @nome, respostas ás túas publicacións e temas, mensaxes, etc" none: "Non é posíbel cargar as notificacións neste intre" @@ -1060,11 +1051,7 @@ gl: jump_reply_up: ir a unha resposta anterior jump_reply_down: ir a unha resposta posterior deleted: "Eliminouse o tema" - auto_close_notice: "Este tema pechará automaticamente en %{timeLeft}." - auto_close_notice_based_on_last_post: "Este tema pechará %{duration} despois da última resposta." auto_close_title: 'Axustes do peche automático' - auto_close_save: "Gardar" - auto_close_remove: "arriba" progress: title: progreso do tema go_top: "principio" @@ -1084,7 +1071,6 @@ gl: '2_8': 'Recibirás notificacións porque segues esta categoría.' '2_4': 'Recibirás notificacións porque publicaches unha resposta neste tema.' '2_2': 'Recibirás notificacións porque segues este tema.' - '2': 'Recibirás notificacións porque liches este tema.' '1_2': 'Notificarémosche se alguén menciona o teu @nome ou che responde.' '1': 'Notificarémosche se alguén menciona o teu @nome ou che responde.' '0_7': 'Estás ignorando todas as notificacións desta categoría.' @@ -1120,7 +1106,6 @@ gl: open: "Abrir tema" close: "Pechar tema" multi_select: "Seleccionar publicacións..." - auto_close: "Pechar automaticamente..." pin: "Pegar tema..." unpin: "Despegar tema..." unarchive: "Desarquivar tema" @@ -1877,32 +1862,12 @@ gl: customize: title: "Personalizar" long_title: "Personalización do sitio" - css: "CSS" - header: "Cabeceira" - top: "Destacados" - footer: "Pé de páxina" - embedded_css: "CSS encaixado" - head_tag: - text: "" - title: "HTML que se inserirá antes da etiqueta " - body_tag: - text: "" - title: "HTML que se inserirá antes da etiqueta " - override_default: "Non incluír folla de estilo estándar" - enabled: "Activado?" preview: "previsualizar" - undo_preview: "eliminar previsualización" - rescue_preview: "estilo predeterminado" - explain_preview: "Ver o sitio con esta folla de estilo personalizada" - explain_undo_preview: "Volver á folla de estilo personalizada activada actualmente" - explain_rescue_preview: "Ver o sitio coa folla de estilo predeterminada" save: "Gardar" new: "Novo" new_style: "Novo estilo" import: "Importar" - import_title: "Seleccionar un ficheiro ou pegar un texto" delete: "Eliminar" - delete_confirm: "Queres eliminar esta personalización?" about: "Modificar as follas de estilo CSS e as cabeceiras HTML do sitio. Engadir unha personalización para comezar." color: "Cor" opacity: "Opacidade" @@ -1915,13 +1880,9 @@ gl: none_selected: "Selecciona un modelo de correo-e para comezar a edición." revert: "Reverter os cambios" revert_confirm: "Confirmas a reversión dos cambios?" - css_html: - title: "CSS/HTML" - long_title: "Personalizacións do CSS e do HTML" colors: title: "Cores" long_title: "Esquemas de cor" - about: "Modifica as cores usadas no sitio sen ter que escribir en CSS. Engade un esquema para comezar." new_name: "Novo esquema de cores" copy_name_prefix: "Copiar de" delete_confirm: "Queres eliminar este esquema de cor?" @@ -2054,8 +2015,6 @@ gl: change_trust_level: "cambiar nivel de confianza" change_username: "cambiar nome do usuario" change_site_setting: "cambiar axuste do sitio" - change_site_customization: "cambiar a personalización do sitio" - delete_site_customization: "eliminar a personalización do sitio" change_site_text: "cambiar o texto do sitio" suspend_user: "suspender usuario" unsuspend_user: "non suspender usuario" @@ -2178,8 +2137,6 @@ gl: blocked: "Bloqueado?" staged: "Transitorio?" show_admin_profile: "Administración" - edit_title: "Editar título" - save_title: "Gardar título" refresh_browsers: "Forzar a actualización do navegador" refresh_browsers_message: "Mensaxe enviada a todos os clientes." show_public_profile: "Amosar o perfil público" diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml index 77720ccfa8..12e7570210 100644 --- a/config/locales/client.he.yml +++ b/config/locales/client.he.yml @@ -101,6 +101,7 @@ he: other: "אחרי %{count} שנים" previous_month: 'חודש קודם' next_month: 'חודש הבא' + placeholder: בחרו תאריך share: topic: 'שתפו קישור לנושא זה' post: 'פוסט #%{postNumber}' @@ -143,6 +144,8 @@ he: emails_are_disabled: "כל הדוא\"ל היוצא נוטרל באופן גורף על ידי מנהל אתר. שום הודעת דוא\"ל, מכל סוג שהוא, לא תשלח." bootstrap_mode_enabled: "כדי להקל על הקמת האתר החדש שלכם, אתם במצב איתחול-ראשוני. כל המשתמשים החדשים יקבלו רמת אמון 1 ויקבלו תמצות יומי במייל. אפשרות זו תכובה אוטומטית כאשר יהיו יותר מ %{min_users} משתמשים." bootstrap_mode_disabled: "מצב איתחול-ראשוני יכובה ב 24 השעות הקרובות." + themes: + default_description: "ברירת מחדל" s3: regions: us_east_1: "מזרח ארה\"ב (צפון וירג'יניה)" @@ -150,6 +153,7 @@ he: us_west_2: "מערב ארה\"ב (ארגון)" us_gov_west_1: "AWS GovCloud (ארה״ב)" eu_west_1: "האיחוד האירופי (אירלנד)" + eu_west_2: "אירופה (לונדון)" eu_central_1: "האיחוד האירופי (פרנקפורט)" ap_southeast_1: "אסיה הפסיפית (סינגפור)" ap_southeast_2: "אסיה הפסיפית (סידני)" @@ -386,6 +390,7 @@ he: posts: "פוסטים" mentions: "אזכורים" messages: "הודעות" + notification_level: "ברירת מחדל של רמת התראות להודעות קבוצה" alias_levels: title: "מי יכולים לשלוח מסרים ו@לאזכר בקבוצה זו?" nobody: "אף אחד" @@ -570,6 +575,7 @@ he: revoke_access: "שלילת גישה" undo_revoke_access: "ביטול שלילת גישה" api_approved: "אושרו:" + theme: "תמה" staff_counters: flags_given: "דגלים שעוזרים" flagged_posts: "פסטים מדוגלים" @@ -1081,15 +1087,6 @@ he: title: "שחכתם להוסיף נמענים?" body: "כרגע ההודעה הזו נשלחת רק אליכם!" admin_options_title: "אפשרויות צוות אופציונליות לנושא זה" - auto_close: - label: "מועד סגירה אוטומטית של נושא:" - error: "הזינו בבקשה ערך תקין." - based_on_last_post: "לא לסגור עד שהפוסט האחרון בנושא זה יהיה לפחות בגיל זה." - all: - examples: 'הזינו מספר שעות (24), שעה מדוייקת (17:30) או חותמת זמן (2013-11-22 14:00).' - limited: - units: "(# מספר שעות)" - examples: 'הזינו מספר שעות (24).' notifications: title: "התראות אודות אזכור @שם, תגובות לפוסטים ולנושאים שלכם, הודעות, וכד'" none: "לא ניתן לטעון כעת התראות." @@ -1343,12 +1340,18 @@ he: topic_status_update: title: "קביעת שעון עצר לנושא" save: "קביעת שעון עצר" - time: "זמן:" + num_of_hours: "מספר שעות:" + remove: "הסרת שעון עצר" publish_to: "פרסום ל:" + when: "מתי:" auto_update_input: - limited: - units: "(# שעות)" - examples: 'הכניסו מספר שעות (24).' + later_today: "בהמשך היום" + tomorrow: "מחר" + later_this_week: "בהמשך השבוע" + this_weekend: "בסוף שבוע זה" + next_week: "בשבוע הבא" + pick_date_and_time: "בחרו תאריך ושעה" + set_based_on_last_post: "סגירה מבוססת על הפוסט האחרון" publish_to_category: title: "תזמון פרסום" temp_open: @@ -1359,7 +1362,14 @@ he: title: "סגירה זמנית" auto_close: title: "סגירה אוטומטית של נושא" + label: "שעות סגירת שעות אוטומטית:" error: "אנא הכניסו ערך תקין." + based_on_last_post: "אל תסגרו עד שהפוסט האחרון בנושא הוא לפחות בגיל זה." + status_update_notice: + auto_open: "נושא זה ייפתח אוטומטית %{timeLeft}." + auto_close: "נושא זו ייסגר אוטומטית %{timeLeft}." + auto_publish_to_category: "נושא זה יפורסם ל-#%{categoryName} %{timeLeft}." + auto_close_based_on_last_post: "נושא זה ייסגר %{duration} אחרי התגובה האחרונה." auto_close_title: 'הגדרות סגירה אוטומטית' auto_close_immediate: one: "הפוסט האחרון בנושא הוא כבר בן שעה, אז הנושא ייסגר מיידית." @@ -1390,10 +1400,10 @@ he: '3_2': 'תקבלו התראות כיוון שאתם עוקבים אחרי הנושא הזה.' '3_1': 'תקבלו התראות כיוון שאתם יצרתם את הנושא הזה.' '3': 'תקבלו התראות כיוון שאתם עוקבים אחרי הנושא זה.' - '2_8': 'תקבלו התראות כיוון שאתם עוקבים אחר קטגוריה זו.' - '2_4': 'תקבלו התראות כיוון שפרסמתם תגובה לנושא זה.' - '2_2': 'תקבלו התראות כיוון שאתם עוקבים אחרי הנושא הזה.' - '2': 'תקבלו התראות כיוון שקראתם את הנושא הזה.' + '2_8': 'תראו ספירה של תגובות חדשות כיוון שאתם עוקבים אחר קטגוריה זו.' + '2_4': 'תראו ספירה של תגובות חדשות כיוון שפרסמתם תגובה לנושא זה.' + '2_2': 'תראו ספירה של תגובות חדשות כיוון שאתם עוקבים אחר נושא זה:' + '2': 'אתם תראו ספירה של תגובות חדשות כיוון שקראתם נושא זה.' '1_2': 'תקבלו התראה אם מישהו יזכיר את @שם_המשתמש/ת שלכם או ישיב לכם.' '1': 'תקבלו התראה אם מישהו יזכיר את @שם_המשתמש/ת שלכם או ישיב לכם.' '0_7': 'אתם מתעלמים מכל ההתראות בקטגוריה זו.' @@ -1429,7 +1439,7 @@ he: open: "פתיחת נושא" close: "סגירת נושא" multi_select: "בחרו פוסטים..." - auto_close: "סגירה אוטומטית..." + timed_update: "קביעת שעון עצר נושא..." pin: "נעיצת נושא..." unpin: "שחרור נעיצת נושא..." unarchive: "הוצאת נושא מארכיון" @@ -2546,36 +2556,22 @@ he: customize: title: "התאמה אישית" long_title: "התאמה של האתר" - css: "CSS" - header: "כותרת" - top: "למעלה" - footer: "כותרת תחתית" - embedded_css: "Embedded CSS" - head_tag: - text: "" - title: "קוד HTML שיוכנס לפני התגית " - body_tag: - text: "" - title: "קוד HTML שיוכנס לפני התגית " - override_default: "אל תכלול את ה-Stylesheet הסטנדרטי" - enabled: "מאופשר?" preview: "תצוגה מקדימה" - undo_preview: "הסרת התצוגה המקדימה" - rescue_preview: "ברירת מחדל סגנונית" - explain_preview: "הצג את האתר על פי גיליון הסגנונות המותאם הזה" - explain_undo_preview: "חזרה לגיליון הסגנונות המותאם המופעל כרגע" - explain_rescue_preview: "צפיה באתר עם גליון הסגנונות העיצובי של ברירת המחדל" + explain_preview: "צפו באתר עם סכמה זו" save: "שמור" new: "חדש" new_style: "סגנון חדש" import: "יבוא" - import_title: "בחרו קובץ או הדביקו טקסט" delete: "מחק" - delete_confirm: "מחק את ההתאמה הזו?" + delete_confirm: "למחוק תמה זאת?" about: "שינוי סגנונות CSS וכותרות HTML באתר. הוספת התאמות כדי להתחיל לערוך." color: "צבע" opacity: "שקיפות" copy: "העתק" + copy_to_clipboard: "העתקה ללוח" + copied_to_clipboard: "הועתק ללוח" + copy_to_clipboard_error: "שגיאה בהעתקת מידע ללוח" + theme_owner: "לא ניתן לעריכה, שייך ל:" email_templates: title: "תבניות דואר אלקטרוני" subject: "נושא" @@ -2584,13 +2580,69 @@ he: none_selected: "בחרו תבנית דואר אלקטרוני לעריכה." revert: "ביטול שינויים" revert_confirm: "האם ברצונכם לבטל את השינויים?" - css_html: - title: "CSS/HTML" - long_title: "התאמת CSS ו-HTML" + theme: + import_theme: "ייבוא תמה" + customize_desc: "התאמה אישית:" + title: "תמות" + long_title: "התאמת צבעית, CSS ותכני HTML של האתר שלכם" + edit: "עריכה" + edit_confirm: "זוהי תמה מרוחקת, אם אתם עורכים CSS/HTML השינויים שלכם יימחקו בפעם הבאה שתעדכנו את התמה." + common: "משותף" + desktop: "מחשב-שולחני" + mobile: "נייד" + preview: "תצוגה מקדימה" + is_default: "תמה מאופשרת כברירת מחדל" + user_selectable: "תמה ניתנת לבחירה על ידי משתמשים" + color_scheme: "סכמת צבעים" + color_scheme_select: "בחירת צבעים לשימוש על ידי תמה" + custom_sections: "אזורים מותאמים אישית:" + theme_components: "רכיבי התמה" + child_themes_check: "תמה כוללת תמות בנות אחרות" + css_html: "CSS/HTML מותאמים" + edit_css_html: "עריכת CSS/HTML" + edit_css_html_help: "לא ערכתם אף CSS או HTML" + import_web_tip: "מאגר שמכיל תמה" + import_file_tip: "קובץ .dcstyle.json שמכיל תמה" + about_theme: "אודות התמה" + license: "רישיון" + component_of: "התמה היא רכיב של:" + update_to_latest: "עדכון לגרסה אחרונה" + check_for_updates: "בדיקת עדכונים" + updating: "מעדכנים..." + up_to_date: "התמה מעודכנת, נבדקה לאחרונה ב:" + add: "הוספה" + commits_behind: + one: "התמה נמצאת 1 עדכונים מאחור!" + other: "התמה נמצאת {{count}} עדכונים מאחור!" + scss: + text: "CSS" + title: "עריכת CSS מותאם, אנחנו מקבלים כל סגנון CSS ו SCSS תקני" + header: + text: "כותרת" + title: "הכניסו HTML להצגה מעל הכותרת" + after_header: + text: "אחרי הכותרת" + title: "הכניסו HTML להצגה בכל הדפים אחרי הכותרת" + footer: + text: "תחתית" + title: "הכניסו HTML להצגה בתחתית הדף" + embedded_scss: + text: "CSS משולב" + title: "הכניסו CSS מותאם לשליחה עם הגרסה המשולבת של ההערות" + head_tag: + text: "" + title: "HTML שיוכנס לפני התג " + body_tag: + text: "" + title: "HTML שיוכנס לפני התג " colors: + select_base: + title: "בחרו סכמת צבעים בסיסית" + description: "סכמת בסיס:" title: "צבעים" + edit: "עריכת סכמות צבעים" long_title: "סכמת צבעים" - about: "סכמת צבעים מאפשרת לך לשנות את הצבעים שבשימוש האתר ללא כתיבת קוד CSS. בחרו או הוסיפו סכימה אחת כדי להתחיל." + about: "שינוי הצבעים בהם נעשה שימוש על ידי התמות שלכם. יצירת סכמת צבעים חדשה כדי להתחיל." new_name: "סכמת צבעים חדשה" copy_name_prefix: "העתק של" delete_confirm: "מחק את סכמת הצבעים הזאת?" @@ -2729,8 +2781,8 @@ he: change_trust_level: "שנוי רמת אמון" change_username: "שינוי שם משתמש/ת" change_site_setting: "שנוי הגדרות אתר" - change_site_customization: "שנוי התאמת אתר" - delete_site_customization: "מחק התאמת אתר" + change_theme: "שינוי תמה" + delete_theme: "מחיקת תמה" change_site_text: "שינוי טקסט אתר" suspend_user: "השעה משתמש" unsuspend_user: "בטל השהיית משתמש" @@ -2865,8 +2917,6 @@ he: blocked: "חסום?" staged: "מועמד?" show_admin_profile: "מנהל ראשי" - edit_title: "ערוך כותרת" - save_title: "שמור כותרת" refresh_browsers: "הכרח רענון דפדפן" refresh_browsers_message: "ההודעה נשלחה לכל הלקוחות!" show_public_profile: "הצג פרופיל פומבי" @@ -2877,6 +2927,7 @@ he: logged_out: "המשתמש/ת התנתקו בכל המכשירים" revoke_admin: 'שלול ניהול ראשי' grant_admin: 'הענקת ניהול' + grant_admin_confirm: "שלחנו אליכם מייל כדי לוודא את האדמיניסטרטור החדש. בבקשה פיתחו אותו ועיקבו אחר ההוראות." revoke_moderation: 'שללו הנחיה' grant_moderation: 'הענקת הנחיה' unblock: 'ביטול חסימה' diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index 3b265df219..b289cf0e3a 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -135,6 +135,9 @@ it: visible: enabled: 'listato %{when}' disabled: 'delistato %{when}' + banner: + enabled: 'lo ha reso un annuncio il %{when}. Apparirà in cima ad ogni pagina finché non verrà chiuso dall''utente. ' + disabled: 'ha rimosso questo banner il %{when}. Non apparirà più in cima ad ogni pagina.' topic_admin_menu: "azioni amministrative sull'argomento" wizard_required: "Benvenuto al tuo nuovo Discourse! Inizia la procedura guidata di configurazione ✨" emails_are_disabled: "Tutte le email in uscita sono state disabilitate a livello globale da un amministratore. Non sarà inviata nessun tipo di notifica via email." @@ -363,7 +366,6 @@ it: automatic_group: Gruppo Automatico closed_group: Gruppo Chiuso is_group_user: "Sei un membro di questo gruppo" - allow_membership_requests: "Permetti agli utenti di richiedere l'invito ai proprietari del gruppo (il gruppo deve essere menzionabile da chiunque)" membership: "Iscrizione" name: "Nome" user_count: "Numero di Membri" @@ -773,7 +775,7 @@ it: one: " ricevuto" other: " ricevuti" days_visited: - one: "Giorno visitato" + one: "giorno di frequenza" other: "giorni di frequenza" posts_read: one: "messaggio letto" @@ -1060,15 +1062,6 @@ it: title: "Hai dimenticato di aggiungere i destinatari?" body: "Per ora il messaggio sarà inviato solo a te stesso!" admin_options_title: "Impostazioni dello staff opzionali per l'argomento" - auto_close: - label: "Tempo per auto-chiusura argomento:" - error: "Inserisci un valore valido." - based_on_last_post: "Non chiudere finché l'ultimo messaggio dell'argomento non è almeno altrettanto vecchio." - all: - examples: 'Inserisci un numero di ore (24), un orario assoluto (17:30) o un timestamp (2013-11-22 14:00).' - limited: - units: "(n° di ore)" - examples: 'Inserisci il numero di ore (24).' notifications: title: "notifiche di menzioni @nome, risposte ai tuoi messaggi e argomenti ecc." none: "Impossibile caricare le notifiche al momento." @@ -1311,11 +1304,7 @@ it: jump_reply_up: passa a una risposta precedente jump_reply_down: passa a una risposta successiva deleted: "L'argomento è stato cancellato" - auto_close_notice: "Questo argomento si chiuderà automaticamente %{timeLeft}." - auto_close_notice_based_on_last_post: "Questo argomento si chiuderà %{duration} dopo l'ultima risposta." auto_close_title: 'Impostazioni di auto-chiusura' - auto_close_save: "Salva" - auto_close_remove: "Non chiudere automaticamente questo argomento" auto_close_immediate: one: "L'ultimo messaggio nell'argomento ha già 1 ora, per cui l'argomento verrà chiuso immediatamente." other: "L'ultimo messaggio nell'argomento ha già %{count} ore, per cui l'argomento verrà chiuso immediatamente." @@ -1348,7 +1337,6 @@ it: '2_8': 'Riceverai notifiche perché stai seguendo questa categoria.' '2_4': 'Riceverai notifiche perché hai pubblicato una risposta a questo argomento.' '2_2': 'Riceverai notifiche perché stai seguendo questo argomento.' - '2': 'Riceverai notifiche perché hai letto questo argomento.' '1_2': 'Riceverai notifiche se qualcuno menziona il tuo @nome o ti risponde.' '1': 'Riceverai notifiche se qualcuno menziona il tuo @nome o ti risponde.' '0_7': 'Stai ignorando tutte le notifiche di questa categoria.' @@ -1384,7 +1372,6 @@ it: open: "Apri Argomento" close: "Chiudi Argomento" multi_select: "Seleziona Messaggi..." - auto_close: "Chiudi Automaticamente..." pin: "Appunta Argomento..." unpin: "Spunta Argomento..." unarchive: "De-archivia Argomento" @@ -2459,32 +2446,12 @@ it: customize: title: "Personalizza" long_title: "Personalizzazioni Sito" - css: "CSS" - header: "Intestazione" - top: "Alto" - footer: "Fondo pagina" - embedded_css: "CSS incorporato" - head_tag: - text: "" - title: "HTML da inserire prima del tag " - body_tag: - text: "" - title: "HTML da inserire prima del tag " - override_default: "Non includere fogli di stile standard" - enabled: "Attivo?" preview: "anteprima" - undo_preview: "rimuovi anteprima" - rescue_preview: "stile default" - explain_preview: "Visualizza il sito con questo foglio di stile personalizzato" - explain_undo_preview: "Torna al foglio di stile personalizzato attualmente attivo." - explain_rescue_preview: "Visualizza il sito con il foglio di stile predefinito" save: "Salva" new: "Nuovo" new_style: "Nuovo Stile" import: "Importare" - import_title: "Seleziona un file o incolla del testo" delete: "Cancella" - delete_confirm: "Cancella questa personalizzazione?" about: "Modifica i fogli di stile CSS e le intestazioni HTML del sito. Aggiungi una personalizzazione per iniziare." color: "Colore" opacity: "Opacità" @@ -2497,13 +2464,9 @@ it: none_selected: "Scegli un modello di e-mail per iniziare la modifica." revert: "Annulla Cambiamenti" revert_confirm: "Sei sicuro di voler annullare i cambiamenti?" - css_html: - title: "CSS/HTML" - long_title: "Personalizzazioni CSS e HTML" colors: title: "Colori" long_title: "Combinazioni Colori" - about: "Modifica i colori utilizzati sul sito senza scrivere CSS. Aggiungi una combinazione per iniziare." new_name: "Nuova Combinazione Colori" copy_name_prefix: "Copia di" delete_confirm: "Eliminare questa combinazione di colori?" @@ -2642,8 +2605,6 @@ it: change_trust_level: "cambia livello esperienza" change_username: "cambia nome utente" change_site_setting: "modifica le impostazioni del sito" - change_site_customization: "modifica la personalizzazione del sito" - delete_site_customization: "cancella la personalizzazione del sito" change_site_text: "cambia il testo del sito" suspend_user: "utente sospeso" unsuspend_user: "utente riattivato" @@ -2778,8 +2739,6 @@ it: blocked: "Bloccato?" staged: "Temporaneo?" show_admin_profile: "Amministratore" - edit_title: "Modifica Titolo" - save_title: "Salva Titolo" refresh_browsers: "Forza l'aggiornamento del browser" refresh_browsers_message: "Messaggio inviato a tutti i client!" show_public_profile: "Mostra Profilo Pubblico" diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml index c6d2623cdf..aaa6607e29 100644 --- a/config/locales/client.ja.yml +++ b/config/locales/client.ja.yml @@ -292,7 +292,6 @@ ja: add: "追加" automatic_group: 自動作成グループ closed_group: クローズドグループ - allow_membership_requests: "ユーザーがグループオーナーに対してメンバーシップリクエストを送信できるようにする(全ユーザーがグループへメンション出来るようにする必要があります。)" membership: "メンバーシップ" user_count: "メンバー数" selector_placeholder: "メンバーを追加" @@ -841,15 +840,6 @@ ja: modal_cancel: "キャンセル" cant_send_pm: "%{username}へメッセージを送ることはできません。" admin_options_title: "このトピックの詳細設定" - auto_close: - label: "このトピックを自動的にクローズする時間:" - error: "正しい値を入力してください。" - based_on_last_post: "このトピックの新しい投稿が古くなるまではクローズしない" - all: - examples: '時間 (例: 24), 時刻(例: 17:30 ), タイムスタンプ(2013-11-22 14:00) を入力してください' - limited: - units: "(# of hours)" - examples: '時間(24)を入力してください' notifications: title: "@ユーザ名 のメンション、投稿やトピックへの返信、メッセージなどの通知" none: "通知はありません" @@ -1020,11 +1010,7 @@ ja: jump_reply_up: 以前の返信へジャンプ jump_reply_down: 以後の返信へジャンプ deleted: "トピックは削除されました" - auto_close_notice: "このトピックはあと%{timeLeft}で自動的にクローズします。" - auto_close_notice_based_on_last_post: "このトピックは最後の返信から%{duration} 経つとクローズします" auto_close_title: 'オートクローズの設定' - auto_close_save: "保存" - auto_close_remove: "このトピックを自動でクローズしない" timeline: back_description: "未読の最終投稿へ戻る" progress: @@ -1047,7 +1033,6 @@ ja: '2_8': 'このカテゴリを追跡しているため通知されます。' '2_4': 'このトピックに返信したため通知されます。' '2_2': 'このトピックを追跡中のため通知されます。' - '2': 'このトピックを閲覧したため通知されます。' '1_2': '他のユーザからメンションされた場合、投稿へ返信された場合に通知します。' '1': '他のユーザからメンションされた場合、投稿へ返信された場合に通知します。' '0_7': 'このカテゴリに関して一切通知を受け取りません。' @@ -1083,7 +1068,6 @@ ja: open: "トピックをオープン" close: "トピックをクローズする" multi_select: "投稿を選択" - auto_close: "自動でクローズする..." pin: "トピックを固定" unpin: "トピックの固定を解除" unarchive: "トピックのアーカイブ解除" @@ -1842,31 +1826,12 @@ ja: customize: title: "カスタマイズ" long_title: "サイトのカスタマイズ" - css: "CSS" - header: "ヘッダ" - top: "トップ" - footer: "フッター" - head_tag: - text: "" - title: "タグの前に挿入されるHTML" - body_tag: - text: "" - title: "タグの前に挿入されるHTML" - override_default: "標準のスタイルシートを読み込まない" - enabled: "有効にする" preview: "プレビュー" - undo_preview: "プレビューを削除" - rescue_preview: "既定スタイル" - explain_preview: "カスタムスタイルシートでサイトを表示する" - explain_undo_preview: "有効中のカスタムスタイルシートへ戻る" - explain_rescue_preview: "既定スタイルシートでサイトを表示する" save: "保存" new: "新規" new_style: "新しいスタイル" import: "インポート" - import_title: "ファイルを選択するかテキストをペースト" delete: "削除" - delete_confirm: "このカスタマイズ設定を削除しますか?" about: "サイトカスタマイズ設定により、サイトのヘッダとスタイルシートを変更できます。設定を選択するか、編集を開始して新たな設定を追加してください。" color: "カラー" opacity: "透明度" @@ -1876,13 +1841,9 @@ ja: subject: "件名" multiple_subjects: "このメールのテンプレートは複数の件名があります。" none_selected: "編集するメールテンプレートを選択してください。" - css_html: - title: "CSS, HTML" - long_title: "CSS と HTML のカスタマイズ" colors: title: "カラー" long_title: "カラースキーム" - about: "CSSを記述することなくサイトのカラーを変更できます。スキームを追加して始めてください" new_name: "カラースキームを作成" copy_name_prefix: "のコピー" delete_confirm: "このカラースキームを削除してもよろしいですか?" @@ -2005,8 +1966,6 @@ ja: change_trust_level: "トラストレベルを変更" change_username: "ユーザ名変更" change_site_setting: "サイトの設定を変更" - change_site_customization: "サイトのカスタマイズ設定を変更" - delete_site_customization: "サイトのカスタマイズ設定を削除" suspend_user: "ユーザを凍結する" unsuspend_user: "ユーザの凍結を解除する" grant_badge: "バッジを付与" @@ -2120,8 +2079,6 @@ ja: blocked: "ブロック状態" staged: "ステージドモードの状態" show_admin_profile: "アカウントの管理" - edit_title: "タイトルを編集" - save_title: "タイトルを保存" refresh_browsers: "ブラウザを強制更新" refresh_browsers_message: "全てのクライアントにメッセージが送信されました!" show_public_profile: "パブリックプロフィールを見る" diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index f6a4fe6c4c..393d35df4b 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -334,7 +334,6 @@ ko: request: "그룹 참여 요청하기" closed_group: 닫힌 그룹 is_group_user: "당신은 이 그룹의 구성원입니다." - allow_membership_requests: "사용자가 그룹 소유자에게 가입 요청을 하는 것을 허용합니다. (모든 유저가 그룹을 맨션할 수 있어야 함)" membership: "회원제" name: "이름" user_count: "멤버 수" @@ -909,15 +908,6 @@ ko: modal_cancel: "취소" cant_send_pm: "죄송합니다. %{username}님에게 메시지를 보낼 수 없습니다." admin_options_title: "이 주제에 대한 옵션 설정" - auto_close: - label: "주제 자동-닫기 시간:" - error: "유효한 값은 눌러주세요." - based_on_last_post: "적어도 주제의 마지막 글이 이만큼 오래되지 않았으면 닫지 마세요." - all: - examples: '시간을 숫자(24이하)로 입력하거나 분을 포함한 시간(17:30) 혹은 타임스탬프(2013-11-22 14:00) 형식으로 입력하세요.' - limited: - units: "(# 시간)" - examples: '시간에 해당하는 숫자를 입력하세요. (24)' notifications: title: "@name 언급, 글과 주제에 대한 답글, 개인 메시지 등에 대한 알림" none: "현재 알림을 불러올 수 없습니다." @@ -1095,11 +1085,7 @@ ko: jump_reply_up: 이전 답글로 이동 jump_reply_down: 이후 답글로 이동 deleted: "주제가 삭제되었습니다" - auto_close_notice: "이 주제는 곧 자동으로 닫힙니다. %{timeLeft}." - auto_close_notice_based_on_last_post: "이 주제는 마지막 답글이 달린 %{duration} 후 닫힙니다." auto_close_title: '자동으로 닫기 설정' - auto_close_save: "저장" - auto_close_remove: "이 주제를 자동으로 닫지 않기" progress: title: 진행 중인 주제 go_top: "맨위" @@ -1119,7 +1105,6 @@ ko: '2_8': '이 주제를 추적하고 있어서 알림을 받게 됩니다.' '2_4': '이 주제에 답글을 게시하여서 알림을 받게 됩니다.' '2_2': '이 주제를 추적하고 있어서 알림을 받게 됩니다.' - '2': '이 주제를 읽어서 알림을 받게 됩니다. (설정)' '1_2': '누군가 내 @아아디 으로 멘션했거나 내 글에 답글이 달릴 때 알림을 받게 됩니다.' '1': '누군가 내 @아아디 으로 멘션했거나 내 글에 답글이 달릴 때 알림을 받게 됩니다.' '0_7': '이 주제에 관한 모든 알림을 무시하고 있습니다.' @@ -1155,7 +1140,6 @@ ko: open: "주제 열기" close: "주제 닫기" multi_select: "글 선택" - auto_close: "자동으로 닫기..." pin: "주제 고정..." unpin: "주제 고정 취소..." unarchive: "주제 보관 취소" @@ -1862,32 +1846,12 @@ ko: customize: title: "사용자 지정" long_title: "사이트 사용자 지정" - css: "CSS" - header: "헤더" - top: "Top" - footer: "푸터(하단영역)" - embedded_css: "Embedded CSS" - head_tag: - text: "" - title: " 태그 전에 들어갈 HTML" - body_tag: - text: "" - title: " 태그 전에 들어갈 HTML" - override_default: "표준 스타일 시트를 포함하지 마십시오" - enabled: "사용가능?" preview: "미리 보기" - undo_preview: "미리보기 삭제" - rescue_preview: "기본 스타일" - explain_preview: "이 커스텀 스타일시트를 적용한 상태로 사이트를 봅니다." - explain_undo_preview: "현재 적용되어 있는 커스톰 스타일시트로 돌아갑니다." - explain_rescue_preview: "기본 스타일시트를 적용한 상태로 사이트를 봅니다." save: "저장" new: "새 사용자 지정" new_style: "새로운 스타일" import: "가져오기" - import_title: "파일을 선택하거나 텍스트를 붙여넣으세요" delete: "삭제" - delete_confirm: "이 정의를 삭제 하시겠습니까?" about: "사이트 Customization은 사이트의 스타일시트와 해더를 수정할 수 있게 해줍니다. 새로운 것을 추가하거나 기존 것을 선택해서 편집하세요." color: "색" opacity: "투명도" @@ -1900,13 +1864,9 @@ ko: none_selected: "편집하려는 이메일 템플릿을 선택하세요." revert: "변경사항 취소" revert_confirm: "정말로 변경사항을 되돌리시겠습니까?" - css_html: - title: "CSS/HTML" - long_title: "CSS, HTML 사용자 정의" colors: title: "색상" long_title: "색상 Schemes" - about: "CSS 작성 없이 사이트에 사용되는 색을 수정합니다. 시작하려면 Scheme을 추가하세요." new_name: "새로운 색 조합" copy_name_prefix: "복사본" delete_confirm: "이 컬러 스키마를 제거합니까?" @@ -2037,8 +1997,6 @@ ko: change_trust_level: "회원등급 변경" change_username: "아이디 변경" change_site_setting: "사이트 설정 변경" - change_site_customization: "사이트 커스텀화 변경" - delete_site_customization: "사이트 커스텀화 삭제" change_site_text: "site text 변경" suspend_user: "suspend user" unsuspend_user: "unsuspend user" @@ -2157,8 +2115,6 @@ ko: blocked: "블락" staged: "격리조치?" show_admin_profile: "관리자" - edit_title: "제목 수정" - save_title: "제목 저장" refresh_browsers: "브라우저 새로 고침" refresh_browsers_message: "모든 클라이언트에게 메시지 보내기" show_public_profile: "공개 프로필 보기" diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml index 71eb0b4c8d..15f5bd408c 100644 --- a/config/locales/client.nb_NO.yml +++ b/config/locales/client.nb_NO.yml @@ -143,6 +143,8 @@ nb_NO: emails_are_disabled: "All utgående e-post har blitt deaktivert globalt av en administrator. Ingen e-postvarslinger vil bli sendt." bootstrap_mode_enabled: "For å gjøre det enklere å lansere det nye nettstedet ditt er det i bootstrap-modus. Alle nye brukere vil få tillitsnivå 1 og få daglige oppdateringer på e-post aktivert. Dette vil automatisk bli slått av når antallet brukere overstiger %{min_users}." bootstrap_mode_disabled: "Bootstrap modus vil bli deaktivert i løpet av de neste 24 timene." + themes: + default_description: "Forvalg" s3: regions: us_east_1: "USA øst (N. Virginia)" @@ -150,6 +152,7 @@ nb_NO: us_west_2: "USA vest (Oregon)" us_gov_west_1: "AWS GovCloud (USA)" eu_west_1: "EU (Irland)" + eu_west_2: "EU (London)" eu_central_1: "EU (Frankfurt)" ap_southeast_1: "Asia/Stillehavsregionen (Singapore)" ap_southeast_2: "Asia/Stillehavsregionen (Sydney)" @@ -570,6 +573,7 @@ nb_NO: revoke_access: "Trekk tilbake tilgang" undo_revoke_access: "Angre \"trekk tilbake tilgang\"" api_approved: "Godkjent:" + theme: "Drakt" staff_counters: flags_given: "nyttige rapporteringer" flagged_posts: "rapporterte innlegg" @@ -1081,15 +1085,6 @@ nb_NO: title: "Glemte du å legge til mottagere?" body: "Nå sender du denne meldingen bare til deg selv!" admin_options_title: "Valgfrie emne-instillinger for stab" - auto_close: - label: "Tid for auto-lukking av emnet:" - error: "Skriv inn en gyldig verdi." - based_on_last_post: "Ikke lukk før det siste innlegget i emnet er minst så gammelt." - all: - examples: 'Før inn antall timer (24), absolutt tid (17:30) eller tidsstempel (2013-11-22 14:00).' - limited: - units: "(# timer)" - examples: 'Før inn antall timer (24).' notifications: title: "varsler om at ditt @navn blir nevnt, svar på dine innlegg, emner, meldinger, osv" none: "Notifikasjoner er ikke tilgjengelig for øyeblikket." @@ -2554,32 +2549,14 @@ nb_NO: customize: title: "Tilpasse" long_title: "Nettstedstilpasninger" - css: "CSS" - header: "Header" - top: "Topp" - footer: "Footer" - embedded_css: "Innebygd CSS" - head_tag: - text: "" - title: "HTML som settes inn før taggen." - body_tag: - text: "" - title: "HTML som settes inn før taggen." - override_default: "Ikke inkluder forvalgt stilark" - enabled: "Aktivert?" preview: "forhåndsvisning" - undo_preview: "avbryt forhåndsvisning" - rescue_preview: "forvalgt stil" - explain_preview: "Se nettstedet med dette skreddersydde stilarket" - explain_undo_preview: "Gå tilbake til nåværende aktivert tilpasset stilark" - explain_rescue_preview: "Se nettstedet med forvalgt stilark" + explain_preview: "Se siden iført denne drakten" save: "Lagre" new: "Ny" new_style: "Ny stil" import: "Importer" - import_title: "Velg en fil eller lim inn tekst" delete: "Slett" - delete_confirm: "Slett denne tilpasningen?" + delete_confirm: "Slett denne drakten?" about: "Endre CSS og HTML-headere på nettstedet. Legg til en tilpasning for å starte." color: "Farge" opacity: "Opacity" @@ -2592,13 +2569,68 @@ nb_NO: none_selected: "Velg en e-postmal for å begynne å redigere." revert: "Tilbakestill endringer" revert_confirm: "Er du sikker på at du vil tilbakestille dine endringer?" - css_html: - title: "CSS/HTML" - long_title: "CSS og HTML-tilpasninger" + theme: + import_theme: "Importer drakt" + customize_desc: "Skreddersy:" + title: "Drakter" + long_title: "Gå over farger, CSS- og HTML-innhold på siden din" + edit: "Rediger" + edit_confirm: "Dette er en drakt annensteds hen, hvis du endrer CSS/HTML vil dine endringer gå tapt neste gang du oppdaterer drakten." + common: "Vanlig" + desktop: "Skrivebord" + mobile: "Mobil" + preview: "Forhåndsvis" + is_default: "Drakt påskrudd som forvalg" + user_selectable: "Drakten kan velges av brukerne" + color_scheme: "Fargepalett" + color_scheme_select: "Velg farger å bruke i drakten" + custom_sections: "Egendefinerte valg:" + included_themes: "Inkluderte drakter" + child_themes_check: "Drakten inneholder andre underdrakter" + css_html: "Egendefinert CSS/HTML" + edit_css_html: "Rediger CSS/HTML" + edit_css_html_help: "Du har ikke redigert noe CSS eller HTML" + import_web_tip: "Pakkebrønn inneholdende drakt" + import_file_tip: ".dcstyle.json-fil inneholdende drakt" + about_theme: "Om drakt" + license: "Lisens" + update_to_latest: "Oppdater til seneste" + check_for_updates: "Se etter oppdateringer" + updating: "Oppdaterer…" + up_to_date: "Drakten er oppdatert, sist sjekket:" + add: "Legg til" + commits_behind: + one: "Drakten én utgave foreldet!" + other: "Drakten er {{count}} utgaver foreldet!" + scss: + text: "CSS" + title: "Skriv inn egendefinert CSS, alle gyldige CSS- og SCSS-stiler godtas" + header: + text: "Hode" + title: "Skriv inn HTML å vise over sidehodet" + after_header: + text: "Etter hode" + title: "Skriv inn HTML å vise på alle sider etter hode" + footer: + text: "Fot" + title: "Skriv inn HTML å vise på sidens fot" + embedded_scss: + text: "Innebygd CSS" + title: "Skriv inn egendefinert CSS for å levere med innebygde versjoner av kommentarer" + head_tag: + text: "" + title: "HTML som har blitt smettet inn før -taggen" + body_tag: + text: "" + title: "HTML som vil bli smettet inn før -taggen" colors: + select_base: + title: "Velg hovedpalett" + description: "Hovedpalett:" title: "Farger" + edit: "Rediger fargepaletter" long_title: "Fargepanel" - about: "Endre farger som brukes på nettstedet uten å skrive CSS. Legg til et skjema for å starte." + about: "Endre fargene brukt i draktene dine. Opprett en ny fargepalett for å komme i gang." new_name: "Ny fargepalett" copy_name_prefix: "Kopi av" delete_confirm: "Slett denne fargepaletten?" @@ -2737,8 +2769,8 @@ nb_NO: change_trust_level: "endre tillitsnivå" change_username: "endre brukernavn" change_site_setting: "endre nettstedsinnstilling" - change_site_customization: "endre tilpasninger for nettstedet" - delete_site_customization: "slett tilpasninger for nettstedet" + change_theme: "bytt drakt" + delete_theme: "slett drakt" change_site_text: "endre sidens tekst" suspend_user: "bannlys bruker" unsuspend_user: "gjeninnsett bruker" diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index 4b546bfeca..3a2faa57ee 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -101,6 +101,7 @@ nl: other: "%{count} jaar later" previous_month: 'Vorige maand' next_month: 'Volgende maand' + placeholder: Kies een datum share: topic: 'een koppeling naar dit topic delen' post: 'bericht #%{postNumber}' @@ -143,6 +144,8 @@ nl: emails_are_disabled: "Alle uitgaande e-mail is uitgeschakeld door een beheerder. Er zal geen enkele e-mailmelding worden verstuurd." bootstrap_mode_enabled: "Om het lanceren van uw website makkelijker te maken, bevindt u zich in bootstrapmodus. Aan alle nieuwe gebruikers wordt vertrouwensniveau 1 toegekend, en dagelijkse e-mailsamenvattingen zijn voor hen ingeschakeld. Dit wordt automatisch uitgeschakeld wanneer het totale gebruikersaantal %{min_users} gebruikers overschrijdt." bootstrap_mode_disabled: "De bootstrapmodus zal in de komende 24 uur worden uitgeschakeld." + themes: + default_description: "Standaard" s3: regions: us_east_1: "VS Oost (N. Virginia)" @@ -150,6 +153,7 @@ nl: us_west_2: "VS West (Oregon)" us_gov_west_1: "AWS GovCloud (VS)" eu_west_1: "EU (Ierland)" + eu_west_2: "EU (Londen)" eu_central_1: "EU (Frankfurt)" ap_southeast_1: "Azië Pacifisch (Singapore)" ap_southeast_2: "Azië Pacifisch (Sydney)" @@ -366,7 +370,7 @@ nl: automatic_group: Automatische groep closed_group: Besloten groep is_group_user: "U bent een lid van deze groep" - allow_membership_requests: "Gebruikers toestaan om lidmaatschapsverzoeken naar groepseigenaren te sturen (Iedereen moet de groep hiervoor kunnen noemen)" + allow_membership_requests: "Gebruikers toestaan om lidmaatschapsverzoeken naar groepseigenaren te sturen" membership: "Lidmaatschap" name: "Naam" user_count: "Aantal leden" @@ -386,6 +390,7 @@ nl: posts: "Berichten" mentions: "Vermeldingen" messages: "Berichten" + notification_level: "Standaard meldingsniveau voor groepsberichten" alias_levels: title: "Wie kan deze groep een bericht sturen en taggen?" nobody: "Niemand" @@ -435,7 +440,7 @@ nl: '13': "Inbox" '14': "In wachtrij" categories: - all: "alle categorieën" + all: "Alle categorieën" all_subcategories: "alle" no_subcategory: "geen" category: "Categorie" @@ -564,11 +569,13 @@ nl: muted_users_instructions: "Alle meldingen van deze gebruikers onderdrukken" muted_topics_link: "Genegeerde topics tonen" watched_topics_link: "In de gaten gehouden topics tonen" + tracked_topics_link: "Gevolgde topics tonen" automatically_unpin_topics: "Topics automatisch losmaken als ik de onderkant bereik" apps: "Apps" revoke_access: "Toegang intrekken" undo_revoke_access: "Toegang intrekken ongedaan maken" api_approved: "Goedgekeurd:" + theme: "Thema" staff_counters: flags_given: "behulpzame markeringen" flagged_posts: "gemarkeerde berichten" @@ -657,7 +664,7 @@ nl: title: "Taal van interface" instructions: "Taal van de gebruikersinterface. Deze verandert zodra u de pagina opnieuw laadt." default: "(standaard)" - any: "elke" + any: "alle" password_confirmation: title: "Nogmaals het wachtwoord" last_posted: "Laatste bericht" @@ -884,7 +891,7 @@ nl: enabled_description: "U bekijkt een samenvatting van dit topic: de meeste interessante berichten zoals bepaald door de gemeenschap." description: "Er zijn {{replyCount}} antwoorden." description_time: "Er zijn {{replyCount}} antwoorden met een geschatte leestijd van {{readingTime}} minuten." - enable: 'Samenvatting van dit topic maken' + enable: 'Dit topic samenvatten' disable: 'Alle berichten tonen' deleted_filter: enabled_description: "Dit topic bevat verwijderde berichten, die zijn verborgen." @@ -938,6 +945,10 @@ nl: not_allowed_from_ip_address: "U kunt zich niet aanmelden vanaf dat IP-adres." admin_not_allowed_from_ip_address: "U kunt zich niet aanmelden als beheerder vanaf dat IP-adres." resend_activation_email: "Klik hier om de activeringsmail opnieuw te versturen." + resend_title: "Activeringsmail opnieuw versturen" + change_email: "E-mailadres wijzigen" + provide_new_email: "Geef een nieuw adres op en we sturen uw bevestigingsmail opnieuw." + submit_new_email: "E-mailadres bijwerken" sent_activation_email_again: "We hebben een nieuwe activeringsmail naar {{currentEmail}} gestuurd. Het kan een aantal minuten duren voor deze aankomt; controleer ook uw spammap." to_continue: "Meld u aan" preferences: "U dient aangemeld te zijn om uw gebruikersvoorkeuren te wijzigen." @@ -1076,15 +1087,6 @@ nl: title: "Bent u ontvangers vergeten toe te voegen?" body: "Het bericht wordt nu alleen naar uzelf verstuurd!" admin_options_title: "Optionele stafinstellingen voor dit topic" - auto_close: - label: "Tijd waarna topic automatisch wordt gesloten:" - error: "Voer een geldige waarde in." - based_on_last_post: "Pas sluiten als het laatste bericht in het topic minstens zo oud is" - all: - examples: 'Voer het aantal uur (24), een absolute tijd (17:30) of een tijdstempel (2013-11-22 14:00) in.' - limited: - units: "(# aantal uur)" - examples: 'Voer het aantal uur in (24).' notifications: title: "meldingen van @naam-vermeldingen, reacties op uw berichten en topics, berichten, etc." none: "Meldingen kunnen momenteel niet worden geladen." @@ -1154,6 +1156,7 @@ nl: sort_by: "Sorteren op" relevance: "Relevantie" latest_post: "Nieuwste bericht" + latest_topic: "Nieuwste topic" most_viewed: "Meest bekeken" most_liked: "Meest geliket" select_all: "Alles selecteren" @@ -1204,7 +1207,7 @@ nl: closed: gesloten zijn archived: gearchiveerd zijn noreplies: geen antwoorden bevatten - single_user: een enkele gebruiker bevatten + single_user: maar één gebruiker bevatten post: count: label: Minimaal berichtaantal @@ -1323,7 +1326,7 @@ nl: options: "Topic-opties" show_links: "koppelingen binnen dit topic tonen" toggle_information: "topicdetails in-/uitschakelen" - read_more_in_category: "Wilt u meer lezen? Blader dan door andere topics in {{catLink}} of {{latestLink}}." + read_more_in_category: "Wilt u meer lezen? U kunt door andere topics in {{catLink}} bladeren, of de {{latestLink}}." read_more: "Wilt u meer lezen? {{catLink}} of {{latestLink}}." read_more_MF: "Er { UNREAD, plural, =0 {} one { is 1 ongelezen } other { zijn # ongelezen } } { NEW, plural, =0 {} one { {BOTH, select, true{and } false {is } other{}} 1 nieuw topic} other { {BOTH, select, true{and } false {zijn } other{}} # nieuwe topics} } over, of {CATEGORY, select, true {blader door andere topics in {catLink}} false {{latestLink}} other {}}" browse_all_categories: Alle categorieën bekijken @@ -1332,11 +1335,40 @@ nl: jump_reply_up: naar eerder antwoord springen jump_reply_down: naar later antwoord springen deleted: "Het topic is verwijderd" - auto_close_notice: "Dit topic wordt na %{timeLeft} automatisch gesloten." - auto_close_notice_based_on_last_post: "Dit topic wordt %{duration} na het laatste antwoord gesloten." + topic_status_update: + title: "Topictimer instellen" + save: "Timer instellen" + num_of_hours: "Aantal uren:" + remove: "Timer verwijderen" + publish_to: "Publiceren naar:" + when: "Wanneer:" + auto_update_input: + later_today: "Later vandaag" + tomorrow: "Morgen" + later_this_week: "Later deze week" + this_weekend: "Dit weekend" + next_week: "Volgende week" + pick_date_and_time: "Kies datum en tijd" + set_based_on_last_post: "Sluiten op basis van laatste bericht" + publish_to_category: + title: "Publicatie plannen" + temp_open: + title: "Tijdelijk openen" + auto_reopen: + title: "Topic automatisch openen" + temp_close: + title: "Tijdelijk sluiten" + auto_close: + title: "Topic automatisch sluiten" + label: "Tijden voor automatisch sluiten van topic:" + error: "Voer een geldige waarde in." + based_on_last_post: "Pas sluiten als het laatste bericht in het topic minstens zo oud is" + status_update_notice: + auto_open: "Dit topic zal automatisch %{timeLeft} worden geopend." + auto_close: "Dit topic zal automatisch %{timeLeft} worden gesloten." + auto_publish_to_category: "Dit topic zal %{timeLeft} naar #%{categoryName} worden gepubliceerd." + auto_close_based_on_last_post: "Dit topic zal %{duration} na het laatste antwoord worden gesloten." auto_close_title: 'Instellingen voor automatisch sluiten' - auto_close_save: "Opslaan" - auto_close_remove: "Dit topic niet automatisch sluiten" auto_close_immediate: one: "Het laatste bericht in dit topic is al 1 uur oud, dus het topic zal meteen worden gesloten." other: "Het laatste bericht in dit topic is al %{count} uur oud, dus het topic zal meteen worden gesloten." @@ -1366,10 +1398,10 @@ nl: '3_2': 'U ontvangt meldingen, omdat u dit topic in de gaten houdt.' '3_1': 'U ontvangt meldingen, omdat u dit topic hebt aangemaakt.' '3': 'U ontvangt meldingen, omdat u dit topic in de gaten houdt.' - '2_8': 'U ontvangt meldingen, omdat u deze categorie volgt.' - '2_4': 'U ontvangt meldingen, omdat u een antwoord in dit topic hebt geplaatst.' - '2_2': 'U ontvangt meldingen, omdat u dit topic volgt.' - '2': 'U ontvangt meldingen, omdat u dit topic hebt gelezen.' + '2_8': 'U ziet het aantal nieuwe antwoorden, omdat u deze categorie volgt.' + '2_4': 'U ziet het aantal nieuwe antwoorden, omdat u een antwoord in dit topic hebt geplaatst.' + '2_2': 'U ziet het aantal nieuwe antwoorden, omdat u dit topic volgt.' + '2': 'U ziet het aantal nieuwe antwoorden, omdat u dit topic hebt gelezen.' '1_2': 'U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt.' '1': 'U ontvangt een melding als iemand uw @naam noemt of een bericht van u beantwoordt.' '0_7': 'U negeert alle meldingen in deze categorie.' @@ -1405,7 +1437,7 @@ nl: open: "Topic openen" close: "Topic sluiten" multi_select: "Berichten selecteren..." - auto_close: "Automatisch sluiten..." + timed_update: "Topictimer instellen..." pin: "Topic vastmaken..." unpin: "Topic losmaken..." unarchive: "Topic dearchiveren" @@ -2522,36 +2554,22 @@ nl: customize: title: "Aanpassen" long_title: "Websiteaanpassingen" - css: "CSS" - header: "Koptekst" - top: "Top" - footer: "Voettekst" - embedded_css: "Embedded CSS" - head_tag: - text: "" - title: "HTML die voor de -tag wordt ingevoegd" - body_tag: - text: "" - title: "HTML die voor de -tag wordt ingevoegd" - override_default: "Standaardstylesheet uitsluiten" - enabled: "Ingeschakeld?" preview: "voorbeeld" - undo_preview: "voorbeeld verwijderen" - rescue_preview: "standaardstijl" - explain_preview: "De website bekijken met deze aangepaste stylesheet" - explain_undo_preview: "Terug naar de momenteel ingeschakelde aangepaste stylesheet" - explain_rescue_preview: "De website bekijken met de standaardstylesheet" + explain_preview: "De website bekijken met dit thema ingeschakeld" save: "Opslaan" new: "Nieuw" new_style: "Nieuwe stijl" import: "Importeren" - import_title: "Selecteer een bestand of plak tekst" delete: "Verwijderen" - delete_confirm: "Deze aanpassing verwijderen?" + delete_confirm: "Dit thema verwijderen?" about: "Pas CSS-stylesheets en HTML-kopteksten aan op de website. Voeg een aanpassing toe om te beginnen." color: "Kleur" opacity: "Ondoorzichtigheid" copy: "Kopiëren" + copy_to_clipboard: "Kopiëren naar klembord" + copied_to_clipboard: "Gekopieerd naar klembord" + copy_to_clipboard_error: "Fout bij kopiëren van gegevens naar klembord" + theme_owner: "Niet bewerkbaar, eigendom van:" email_templates: title: "E-mailsjablonen" subject: "Onderwerp" @@ -2560,13 +2578,69 @@ nl: none_selected: "Selecteer een e-mailsjabloon om met bewerken te beginnen." revert: "Wijzigingen ongedaan maken" revert_confirm: "Weet u zeker dat u uw wijzigingen ongedaan wilt maken?" - css_html: - title: "CSS/HTML" - long_title: "CSS- en HTML-aanpassingen" + theme: + import_theme: "Thema importeren" + customize_desc: "Aanpassen:" + title: "Thema's" + long_title: "Kleuren, CSS en HTML-inhoud van uw website aanpassen" + edit: "Bewerken" + edit_confirm: "Dit is een extern thema; als u CSS/HTML bewerkt, worden uw wijzigingen de volgende keer dat u het thema bijwerkt gewist." + common: "Algemeen" + desktop: "Desktop" + mobile: "Mobiel" + preview: "Voorbeeld" + is_default: "Thema is standaard ingeschakeld" + user_selectable: "Thema kan door gebruikers worden geselecteerd" + color_scheme: "Kleurenschema" + color_scheme_select: "Kleuren die door thema worden gebruikt selecteren" + custom_sections: "Aangepaste secties:" + theme_components: "Themaonderdelen" + child_themes_check: "Thema bevat andere onderliggende thema's" + css_html: "Aangepaste CSS/HTML" + edit_css_html: "CSS/HTML bewerken" + edit_css_html_help: "U hebt geen CSS of HTML bewerkt" + import_web_tip: "Repository die thema bevat" + import_file_tip: ".dcstyle.json-bestand dat thema bevat" + about_theme: "Over thema" + license: "Licentie" + component_of: "Thema is een onderdeel van:" + update_to_latest: "Bijwerken naar nieuwste" + check_for_updates: "Controleren op updates" + updating: "Bijwerken..." + up_to_date: "Thema is up-to-date, laatst gecontroleerd:" + add: "Toevoegen" + commits_behind: + one: "Thema loopt 1 doorvoering achter!" + other: "Thema loopt {{count}} doorvoeringen achter!" + scss: + text: "CSS" + title: "Voer aangepaste CSS in; we accepteren alle geldige CSS- en SCSS-stijlen" + header: + text: "Koptekst" + title: "Voer HTML in voor weergave boven websitekoptekst" + after_header: + text: "Na koptekst" + title: "Voer HTML in voor weergave op alle pagina's na koptekst" + footer: + text: "Voettekst" + title: "Voer HTML in voor weergave op paginavoettekst" + embedded_scss: + text: "Embedded CSS" + title: "Voer aangepaste CSS in om met embedded versie van opmerkingen te leveren" + head_tag: + text: "" + title: "HTML die voor de -tag wordt ingevoegd" + body_tag: + text: "" + title: "HTML die voor de -tag wordt ingevoegd" colors: + select_base: + title: "Basiskleurenschema selecteren" + description: "Basisschema:" title: "Kleuren" + edit: "Kleurenschema's bewerken" long_title: "Kleurenschema's" - about: "Met kleurenschema's kunt u de kleuren op de website aanpassen zonder CSS te hoeven gebruiken. Voeg een schema toe om te beginnen." + about: "De door uw thema's gebruikte kleuren aanpassen. Maak een nieuw kleurenschema aan om te beginnen." new_name: "Nieuw kleurenschema" copy_name_prefix: "Kopie van" delete_confirm: "Dit kleurenschema verwijderen?" @@ -2705,8 +2779,8 @@ nl: change_trust_level: "vertrouwensniveau wijzigen" change_username: "gebruikersnaam wijzigen" change_site_setting: "website-instelling wijzigen" - change_site_customization: "website-aanpassing wijzigen" - delete_site_customization: "website-aanpassing verwijderen" + change_theme: "thema wijzigen" + delete_theme: "thema verwijderen" change_site_text: "tekst van website wijzigen" suspend_user: "gebruiker schorsen" unsuspend_user: "schorsing opheffen" @@ -2841,8 +2915,6 @@ nl: blocked: "Geblokkeerd?" staged: "Staged?" show_admin_profile: "Beheerder" - edit_title: "Titel bewerken" - save_title: "Titel opslaan" refresh_browsers: "Vernieuwen in browser forceren" refresh_browsers_message: "Bericht verstuurd aan alle gebruikers!" show_public_profile: "Openbaar profiel tonen" @@ -2853,6 +2925,7 @@ nl: logged_out: "Gebruiker is op alle apparaten afgemeld" revoke_admin: 'Beheerdersrechten intrekken' grant_admin: 'Beheerdersrechten toekennen' + grant_admin_confirm: "We hebben u een e-mail gestuurd om de nieuwe beheerder te verifiëren. Open deze en volg de instructies." revoke_moderation: 'Moderatierechten intrekken' grant_moderation: 'Moderatierechten toekennen' unblock: 'Deblokkeren' diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index 833ef495bf..73fcf4c008 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -415,7 +415,6 @@ pl_PL: automatic_group: Automatyczna grupa closed_group: Zamknięta grupa is_group_user: "Jesteś członkiem tej grupy" - allow_membership_requests: "Pozwalaj użytkownikom wysyłać prośby o członkostwo do właścicieli grup (Wymagane jest aby wszyscy mogli wspominać grupę)" membership: "Członkostwo" name: "Nazwa" user_count: "Ilość użytkowników" @@ -1153,15 +1152,6 @@ pl_PL: title: "Nie zapomniałeś dodać odbiorców?" body: "Aktualnie ta wiadomość będzie wysłana tylko do ciebie!" admin_options_title: "Opcjonalne ustawienia obsługi dla tego tematu" - auto_close: - label: "Automatycznie zamykaj tematy po:" - error: "Podaj poprawną wartość." - based_on_last_post: "Nie zamykaj tematu dopóki od ostatniego wpisu nie upłynie przynajmniej tyle czasu." - all: - examples: 'Podaj godzinę (17:30), liczbę godzin (24) lub konkretną datę i czas (2013-11-22 14:00).' - limited: - units: "(# godzin)" - examples: 'Podaj liczbę godzin (24).' notifications: title: "powiadomienia o wywołanej @nazwie, odpowiedzi do twoich wpisów i tematów, prywatne wiadomości, itp" none: "Nie udało się załadować listy powiadomień." @@ -1430,11 +1420,7 @@ pl_PL: jump_reply_up: przeskocz do wcześniejszej odpowiedzi jump_reply_down: przeskocz do późniejszej odpowiedzi deleted: "Temat został usunięty" - auto_close_notice: "Ten temat zostanie automatycznie zamknięty %{timeLeft}." - auto_close_notice_based_on_last_post: "Ten temat zostanie automatycznie zamknięty %{duration} po ostatniej odpowiedzi." auto_close_title: 'Ustawienia automatycznego zamykania' - auto_close_save: "Zapisz" - auto_close_remove: "Nie zamykaj automatycznie tego tematu" auto_close_immediate: one: "Ostatni wpis w tym temacie został zamieszczony 1 godzinę temu, więc zostanie on natychmiastowo zamknięty." few: "Ostatni wpis w tym temacie został zamieszczony %{hours} godzin temu, więc zostanie on natychmiastowo zamknięty." @@ -1468,7 +1454,6 @@ pl_PL: '2_8': 'Będziesz otrzymywać powiadomienia, ponieważ śledzisz tę kategorię.' '2_4': 'Będziesz otrzymywać powiadomienia, ponieważ jesteś autorem odpowiedzi w tym temacie.' '2_2': 'Będziesz otrzymywać powiadomienia, ponieważ śledzisz ten temat.' - '2': 'Będziesz otrzymywać powiadomienia, ponieważ ten temat został uznany za przeczytany.' '1_2': 'Dostaniesz powiadomienie jedynie, gdy ktoś wspomni twoją @nazwę lub odpowie na twój wpis.' '1': 'Dostaniesz powiadomienie jedynie, gdy ktoś wspomni twoją @nazwę lub odpowie na twój wpis.' '0_7': 'Ignorujesz wszystkie powiadomienia z tej kategorii.' @@ -1504,7 +1489,6 @@ pl_PL: open: "Otwórz temat" close: "Zamknij temat" multi_select: "Wybierz wpisy…" - auto_close: "Zamknij automatycznie…" pin: "Przypnij temat…" unpin: "Odepnij temat…" unarchive: "Przywróć z archiwum" @@ -2722,32 +2706,12 @@ pl_PL: customize: title: "Wygląd" long_title: "Personalizacja strony" - css: "CSS" - header: "Nagłówki" - top: "Nagłówek" - footer: "Stopka" - embedded_css: "Osadzony CSS" - head_tag: - text: "" - title: "Kod HTML, który zostanie umieszczony przed tagiem " - body_tag: - text: "" - title: "Kod HTML, który zostanie umieszczony przed tagiem ." - override_default: "Nie dołączaj standardowego arkusza stylów" - enabled: "Włączone?" preview: "podgląd" - undo_preview: "usuń podgląd" - rescue_preview: " domyślny styl" - explain_preview: "Podejrzyj witrynę z użyciem tego sylesheet'u" - explain_undo_preview: "Wróć do aktualnie aktywnego schematu styli" - explain_rescue_preview: "Zobacz stronę z domyślnym stylem" save: "Zapisz" new: "Nowy" new_style: "Nowy styl" import: "Import" - import_title: "Wybierz plik lub wklej tekst" delete: "Usuń" - delete_confirm: "Usunąć tę personalizację?" about: "Zmień arkusze stylów CSS i nagłówki HTML w witrynie. Dodaj własne ustawienie aby rozpocząć." color: "Kolor" opacity: "Widoczność" @@ -2760,13 +2724,9 @@ pl_PL: none_selected: "Aby rozpocząć edycję, wybierz szablon wiadomości e-mail. " revert: "Cofnij zmiany" revert_confirm: "Czy na pewno chcesz wycofać swoje zmiany?" - css_html: - title: "CSS, HTML" - long_title: "Personalizacja kodu CSS i HTML" colors: title: "Kolory" long_title: "Schematy kolorów" - about: "Zmień kolory strony bez modyfikacji CSS. Dodaj nowy schemat kolorów, aby rozpocząć." new_name: "Nowy schemat kolorów" copy_name_prefix: "Kopia" delete_confirm: "Usunąć ten schemat kolorów?" @@ -2905,8 +2865,6 @@ pl_PL: change_trust_level: "zmiana poziomu zaufania" change_username: "zmień nazwę użytkownika" change_site_setting: "zmiana ustawień serwisu" - change_site_customization: "modyfikacja personalizacji serwisu" - delete_site_customization: "usunięcie personalizacji strony" change_site_text: "zmiana tekstu serwisu" suspend_user: "zawieszenie użytkownika" unsuspend_user: "odwieszenie użytkownika" @@ -3049,8 +3007,6 @@ pl_PL: blocked: "Zablokowany?" staged: "Wystawiony?" show_admin_profile: "Admin" - edit_title: "Edytuj tytuł" - save_title: "Zapisz tytuł" refresh_browsers: "Wymuś odświeżenie przeglądarki" refresh_browsers_message: "Wiadomość wysłana do wszystkich klientów!" show_public_profile: "Pokaż profil publiczny" diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml index 14557e1d99..6e8e78e592 100644 --- a/config/locales/client.pt.yml +++ b/config/locales/client.pt.yml @@ -101,6 +101,7 @@ pt: other: "%{count} anos mais tarde" previous_month: 'Mês Anterior' next_month: 'Mês Seguinte' + placeholder: Escolha uma data share: topic: 'partilhar uma ligação para este tópico' post: 'publicação #%{postNumber}' @@ -136,9 +137,12 @@ pt: enabled: 'listado %{when}' disabled: 'removido da lista %{when}' topic_admin_menu: "Ações administrativas dos Tópicos" + wizard_required: "Bem-vindo ao seu novo Discourse! Vamos começar com o assistente de configuração ✨" emails_are_disabled: "Todos os envios de e-mail foram globalmente desativados por um administrador. Nenhum e-mail de notificação será enviado." bootstrap_mode_enabled: "Para facilitar o lançamento do seu novo sítio mais fácil, encontra-se agora em modo de inicialização. Qualquer utilizador novo terá nível de confiança 1 e resumos de email diários ligados. Este modo será automaticamente desligado quando o número total de utilizadores ultrapassar %{min_users}." bootstrap_mode_disabled: "Modo de inicialização será desligado dentro das próximas 24 horas." + themes: + default_description: "Predefinido" s3: regions: us_east_1: "Este dos E.U.A. (Virgínia do Norte)" @@ -146,6 +150,7 @@ pt: us_west_2: "Oeste dos E.U.A. (Óregon)" us_gov_west_1: "AWS GovCloud (E.U.A.)" eu_west_1: "U.E. (Irlanda)" + eu_west_2: "EU (Londres)" eu_central_1: "U.E. (Francoforte)" ap_southeast_1: "Ásia-Pacífico (Singapura)" ap_southeast_2: "Ásia-Pacífico (Sydney)" @@ -347,6 +352,7 @@ pt: title: "Pedido de Adesão" body: "Eu gostaria de aderir a @%{groupName}." name_placeholder: "Nome do grupo, sem espaços, com as mesmas regras do nome de utilizador" + public: "Permitir aos utilizadores de se juntarem/saírem do grupo livremente (Requere que o grupo seja visível)" empty: posts: "Não existem publicações por membros deste grupo." members: "Não existem membros neste grupo." @@ -358,8 +364,11 @@ pt: join: "Juntar ao Grupo" leave: "Sair do Grupo" request: "Pedir para Entrar no Grupo." + automatic_group: Grupo Automático closed_group: Grupo fechado is_group_user: "Você é membro deste grupo" + allow_membership_requests: "Permitir aos utilizadores enviar pedidos de adesão aos donos do grupo" + membership: "Adesão" name: "Nome" user_count: "Numero de Membros" bio: "Acerca do Grupo" @@ -378,6 +387,7 @@ pt: posts: "Publicações" mentions: "Menções" messages: "Mensagens" + notification_level: "Nível de notificação por omissão para mensagens de grupo" alias_levels: title: "Quem pode mandar mensagens e @mencionar este grupo?" nobody: "Ninguém" @@ -404,6 +414,7 @@ pt: muted: title: "Silenciado" description: "Não será notificado de nada relacionado com novos tópicos neste grupo." + flair_url: "Imagem da Marca de Avatar" user_action_groups: '1': "Gostos Dados" '2': "Gostos Recebidos" @@ -1041,15 +1052,6 @@ pt: title: "Esqueceu-se de adicionar destinatários?" body: "Neste momento esta mensagem está a ser enviada a você apenas!" admin_options_title: "Configurações opcionais do pessoal para este tópico" - auto_close: - label: "Tempo de fecho automático do tópico:" - error: "Por favor introduza um valor válido." - based_on_last_post: "Não fechar até que a última resposta no tópico tenha pelo menos esta idade." - all: - examples: 'Insira o número de horas (24), tempo absoluto (17:30) ou um selo temporal (2013-11-22 14:00).' - limited: - units: "(# de horas)" - examples: 'Introduza o número de horas (24).' notifications: title: "notificações de menções de @nome, respostas às suas publicações e tópicos, mensagens, etc" none: "Impossível de carregar as notificações neste momento." @@ -1292,11 +1294,7 @@ pt: jump_reply_up: avançar para resposta mais recente jump_reply_down: avançar para resposta mais antiga deleted: "Este tópico foi eliminado" - auto_close_notice: "Este tópico vai ser automaticamente encerrado em %{timeLeft}." - auto_close_notice_based_on_last_post: "Este tópico será encerrado %{duration} depois da última resposta" auto_close_title: 'Configurações para Fechar Automaticamente' - auto_close_save: "Guardar" - auto_close_remove: "Não Fechar Este Tópico Automaticamente" auto_close_immediate: one: "A última publicação neste tópico já tem 1 hora, por isso o tópico será fechado imediatamente." other: "A última publicação neste tópico já tem %{count} horas, por isso o tópico será fechado imediatamente." @@ -1329,7 +1327,6 @@ pt: '2_8': 'Receberá notificações porque está a seguir esta categoria.' '2_4': 'Receberá notificações porque publicou uma resposta a este tópico.' '2_2': 'Receberá notificações porque está a seguir este tópico.' - '2': 'Receberá notificações porque leu este tópico.' '1_2': 'Será notificado se alguém mencionar o seu @nome ou responder-lhe.' '1': 'Será notificado se alguém mencionar o seu @nome ou responder-lhe.' '0_7': 'Está a ignorar todas as notificações nesta categoria.' @@ -1365,7 +1362,6 @@ pt: open: "Abrir Tópico" close: "Fechar Tópico" multi_select: "Selecionar Publicações..." - auto_close: "Fechar Automaticamente..." pin: "Fixar Tópico..." unpin: "Desafixar Tópico..." unarchive: "Desarquivar Tópico" @@ -2439,32 +2435,12 @@ pt: customize: title: "Personalizar" long_title: "Personalizações do Sítio" - css: "CSS" - header: "Cabeçalho" - top: "Os Melhores" - footer: "Rodapé" - embedded_css: "CSS incorporado" - head_tag: - text: "" - title: "HTML que será introduzido antes da tag " - body_tag: - text: "" - title: "HTML que será introduzido antes da tag " - override_default: "Não incluir a folha de estilo por defeito" - enabled: "Ativado?" preview: "pré-visualização" - undo_preview: "remover pré-visualização" - rescue_preview: "estilo por defeito" - explain_preview: "Ver o sítio com esta folha de estilo personalizada" - explain_undo_preview: "Voltar atrás para a atual folha de estilo personalizada ativa" - explain_rescue_preview: "Ver o sítio com a folha de estilo por defeito" save: "Guardar" new: "Novo" new_style: "Novo Estilo" import: "Importar" - import_title: "Selecione um ficheiro ou cole texto" delete: "Eliminar" - delete_confirm: "Remover esta personalização?" about: "Modificar folha de estilo CSS e cabeçalhos HTML no sítio. Adicionar personalização para iniciar." color: "Cor" opacity: "Opacidade" @@ -2477,13 +2453,9 @@ pt: none_selected: "Selecione um modelo de email para começar a editar." revert: "Reverter Alterações" revert_confirm: "Tem a certeza que quer reverter as suas alterações?" - css_html: - title: "CSS/HTML" - long_title: "Personalizações CSS e HTML" colors: title: "Cores" long_title: "Esquemas de Cores" - about: "Modificar as cores usadas no sítio sem escrever CSS. Adicionar um esquema para iniciar." new_name: "Novo Esquema de Cores" copy_name_prefix: "Cópia de" delete_confirm: "Apagar este esquema de cor?" @@ -2622,8 +2594,6 @@ pt: change_trust_level: "modificar Nível de Confiança" change_username: "alterar nome de utilizador" change_site_setting: "alterar configurações do sítio" - change_site_customization: "alterar personalização do sítio" - delete_site_customization: "remover personalização do sítio" change_site_text: "alterar texto do sítio" suspend_user: "utilizador suspenso" unsuspend_user: "utilizador não suspenso" @@ -2751,8 +2721,6 @@ pt: blocked: "Bloqueado?" staged: "Temporário?" show_admin_profile: "Administração" - edit_title: "Editar Título" - save_title: "Guardar Título" refresh_browsers: "Forçar atualização da página no browser" refresh_browsers_message: "Mensagem enviada para todos os clientes!" show_public_profile: "Mostrar Perfil Público" diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml index cf333ec741..c81f603a86 100644 --- a/config/locales/client.pt_BR.yml +++ b/config/locales/client.pt_BR.yml @@ -366,7 +366,6 @@ pt_BR: automatic_group: Grupo Automático closed_group: Grupo Fechado is_group_user: "Você é membro deste grupo" - allow_membership_requests: "Permitir que usuários enviem pedidos de adesão a donos do grupo (Requer que todos possam mencionar o grupo)" membership: "Adesão" name: "Nom" user_count: "Número de Membros" @@ -938,6 +937,9 @@ pt_BR: not_allowed_from_ip_address: "Você não pode logar deste endereço IP." admin_not_allowed_from_ip_address: "Você não pode entrar como administrador a partir deste endereço IP." resend_activation_email: "Clique aqui para enviar o email de ativação novamente." + resend_title: "Reenviar E-mail de Ativação" + change_email: "Trocar Endereço de E-mail" + submit_new_email: "Atualizar Endereço de E-mail" sent_activation_email_again: "Nós enviamos mais um email de ativação para você no endereço {{currentEmail}}. Pode ser que demore alguns minutos para chegar; verifique sempre sua caixa de spams." to_continue: "Por favor efetue o login" preferences: "Você precisa estar logado para mudar suas preferências de usuário." @@ -1076,15 +1078,6 @@ pt_BR: title: "Você se esqueceu de adicionar destinatários?" body: "No momento esta mensagem está sendo enviada apenas para si mesmo!" admin_options_title: "Configurações opcionais da equipe para este tópico" - auto_close: - label: "Tempo para fechamento automático do tópico:" - error: "Por favor, digite um valor válido." - based_on_last_post: "Não feche até que o último post no tópico seja o mais velho." - all: - examples: 'Insira o número de horaas (24), hora absoluta (17:30) ou o timestamp (2013-11-22 14:00).' - limited: - units: "(núm. de horas)" - examples: 'Insira o número de horas (24).' notifications: title: "notificações de menção de @name, respostas às suas postagens, tópicos, mensagens, etc" none: "Não foi possível carregar notificações no momento." @@ -1332,11 +1325,7 @@ pt_BR: jump_reply_up: pular para a resposta mais recente jump_reply_down: pular para a resposta mais antiga deleted: "Este tópico foi apagado" - auto_close_notice: "Este tópico vai ser automaticamente fechado em %{timeLeft}." - auto_close_notice_based_on_last_post: "Este tópico fechará %{duration} depois da última resposta." auto_close_title: 'Configurações para fechar automaticamente' - auto_close_save: "Salvar" - auto_close_remove: "Não fechar automaticamente este tópico" auto_close_immediate: one: "A última publicação no tópico já tem 1 hora, portanto ele será fechado imediatamente." other: "A última publicação no tópico já tem %{count} horas, portanto ele será fechado imediatamente." @@ -1369,7 +1358,6 @@ pt_BR: '2_8': 'Você receberá notificações porque você está monitorando essa categoria.' '2_4': 'Você receberá notificações porque postou uma resposta neste tópico.' '2_2': 'Você receberá notificações porque está monitorando este tópico.' - '2': 'Você receberá notificações porque você leu este tópico.' '1_2': 'Você será notificado se alguém mencionar o seu @nome ou responder à sua mensagem.' '1': 'Você será notificado se alguém mencionar o seu @nome ou responder à sua mensagem.' '0_7': 'Você está ignorando todas as notificações nessa categoria.' @@ -1405,7 +1393,6 @@ pt_BR: open: "Abrir tópico" close: "Fechar tópico" multi_select: "Selecionar Mensagens..." - auto_close: "Fechar automaticamente..." pin: "Fixar Tópico..." unpin: "Desafixar Tópico..." unarchive: "Desarquivar tópico" @@ -2521,32 +2508,12 @@ pt_BR: customize: title: "Personalizar" long_title: "Personalizações do Site" - css: "CSS" - header: "Cabeçalho" - top: "Superior" - footer: "Rodapé" - embedded_css: "CSS Incorporada" - head_tag: - text: "" - title: "HTML que será inserido antes da tag " - body_tag: - text: "" - title: "HTML que será inserido antes da tag " - override_default: "Sobrepor padrão?" - enabled: "Habilitado?" preview: "pré-visualização" - undo_preview: "remover preview" - rescue_preview: "estilo padrão" - explain_preview: "Ver o site com o estilo personalizado" - explain_undo_preview: "Voltar para o estilo personalizado atual" - explain_rescue_preview: "Ver o site com o estilo padrão" save: "Guardar" new: "Novo" new_style: "Novo Estilo" import: "Importar" - import_title: "Selecione um arquivo ou cole texto" delete: "Apagar" - delete_confirm: "Apagar esta personalização?" about: "Modificar o CSS e HTML do cabeçalho do site. Adicione uma customização para começar." color: "Cor" opacity: "Opacidade" @@ -2559,13 +2526,9 @@ pt_BR: none_selected: "Selecione um modelo de e-mail para iniciar a edição." revert: "Reverter Alterações" revert_confirm: "Tem certeza de que deseja reverter as alterações?" - css_html: - title: "CSS/HTML" - long_title: "Customizações CSS e HTML" colors: title: "Cores" long_title: "Esquema de Cores" - about: "Modifique as cores usadas no site sem escrever CSS. Adicione um esquema para começar." new_name: "Novo Esquema de Cor" copy_name_prefix: "Copiar de" delete_confirm: "Apagar esse esquema de cor?" @@ -2704,8 +2667,6 @@ pt_BR: change_trust_level: "modificou nível de confiança" change_username: "mudar nome de usuário" change_site_setting: "alterar configurações do site" - change_site_customization: "alterar personalização do site" - delete_site_customization: "remover personalização do site" change_site_text: "alterar texto do site" suspend_user: "suspender usuário" unsuspend_user: "readmitir usuário" @@ -2840,8 +2801,6 @@ pt_BR: blocked: "Bloqueado?" staged: "Testado?" show_admin_profile: "Admin" - edit_title: "Editar Título" - save_title: "Salvar Título" refresh_browsers: "Forçar atualização da página no browser" refresh_browsers_message: "Mensagem enviada para todos os clientes!" show_public_profile: "Mostrar Perfil Público" diff --git a/config/locales/client.ro.yml b/config/locales/client.ro.yml index c08e2c6221..f461ccfbd8 100644 --- a/config/locales/client.ro.yml +++ b/config/locales/client.ro.yml @@ -40,7 +40,7 @@ ro: long_date_with_year_without_time: "DD MMM 'YY" long_date_without_year_with_linebreak: "DD MMM
    HH:mm" long_date_with_year_with_linebreak: "DD MMM 'YY
    HH:mm" - wrap_ago: "%{date} (de) zile în urmă" + wrap_ago: "%{date} în urmă" tiny: half_a_minute: "< 1m" less_than_x_seconds: @@ -157,6 +157,8 @@ ro: emails_are_disabled: "Trimiterea de emailuri a fost dezactivată global de către un administrator. Nu vor fi trimise notificări email de nici un fel." bootstrap_mode_enabled: "Pentru a ușura lansarea site-ului tău ești în modul bootstrap. Toți noii utilizatori vor primi nivelul de încredere 1 și vor avea activată primirea zilnică a unui email-rezumat. Această setare va fi dezactivată automat de îndată ce numărul total de utilizatori depășește %{min_users}." bootstrap_mode_disabled: "Modul bootstrap va fi dezactivat în următoarele 24 de ore." + themes: + default_description: "Implicit" s3: regions: us_east_1: "US East (N. Virginia)" @@ -233,6 +235,7 @@ ro: like_count: "Aprecieri" topic_count: "Subiecte" post_count: "Postări" + user_count: "Utilizatori" active_user_count: "Utilizatori activi" contact: "Contactează-ne" contact_info: "În cazul în care o problemă critică sau alt aspect urgent afectează site-ul, contactează-ne la %{contact_info}." @@ -387,7 +390,6 @@ ro: automatic_group: Grup automat closed_group: Grup închis is_group_user: "Ești membru al acestui grup" - allow_membership_requests: "Permite utilizatorilor să trimită cereri de adăugare ca membri către proprietarii grupului. (Presupune ca toată lumea să aibă posibilitatea de a menționa grupul)" membership: "Apartenență" name: "Nume" user_count: "Număr de membri" @@ -402,6 +404,7 @@ ro: one: "Grup" few: "Grupuri" other: "de grupuri" + activity: "Activitate" members: "Membri" topics: "Subiecte" posts: "Postări" @@ -615,6 +618,8 @@ ro: error: "(eroare)" action: "Trimite email pentru resetare parolă" set_password: "Introdu parolă" + choose_new: "Alege o parolă nouă" + choose: "Alege o parolă" change_about: title: "Schimbare date personale" error: "A apărut o eroare la schimbarea acestei valori." @@ -659,6 +664,7 @@ ro: other: "Vom trimite un email doar dacă nu te-am văzut în ultimele {{count}} de minute." name: title: "Nume" + instructions: "Numele tău complet (opțional)" instructions_required: "Numele tău complet" too_short: "Numele este prea scurt." ok: "Numele tău este OK." @@ -667,6 +673,7 @@ ro: short_instructions: "Ceilalți te pot menționa ca @{{username}}." available: "Numele de utilizator este disponibil." not_available: "Nu este disponibil. Încerci {{suggestion}}?" + not_available_no_suggestion: "Indisponibil" too_short: "Numele de utilizator este prea scurt." too_long: "Numele de utilizator este prea lung." checking: "Verifică disponibilitatea numelui de utilizator..." @@ -777,6 +784,7 @@ ro: same_as_username: "Parolă identică cu numele de utilizator" same_as_email: "Parolă identică cu adresa de email" ok: "Parola dumneavoastră este OK." + instructions: "minimum %{count} caractere" summary: title: "Sumar" stats: "Statistici" @@ -962,6 +970,8 @@ ro: not_allowed_from_ip_address: "Nu te poți conecta cu această adresă IP." admin_not_allowed_from_ip_address: "Nu te poți conecta ca administrator cu această adresă IP." resend_activation_email: "Click aici pentru a retrimite emailul de activare." + change_email: "Schimbă adresa de email" + submit_new_email: "Actualizează adresa de email" sent_activation_email_again: "Am trimis un alt email de activare pentru tine la {{currentEmail}}. Poate dura câteva minute până ajunge; vezi și în secțiunea de spam a mailului." to_continue: "Te rog să te autentifici." preferences: "Trebuie să fii autentificat pentru a schimba preferințele." @@ -1089,15 +1099,6 @@ ro: title: "Ai uitat să adaugi destinatari?" body: "În acest moment mesajul acesta nu este trimis decât către tine însuți!" admin_options_title: "Setări opționale pentru moderatori cu privire la acest subiect" - auto_close: - label: "Închide automat discuția la:" - error: "Introduceţi o valoare validă." - based_on_last_post: "Nu închide subiectul până când ultimul răspuns nu are o vechime de cel puțin" - all: - examples: 'Introdu numărul de ore (24), timpul absolut (17:30) sau data şi ora cu secunde (2013-11-22 14:00).' - limited: - units: "(# de ore)" - examples: 'Introdu numărul de ore (24).' notifications: title: "notificări la menționările @numelui tău, răspunsuri la postările sau subiectele tale, mesaje, etc." none: "Nu pot încărca notificările în acest moment." @@ -1351,11 +1352,10 @@ ro: jump_reply_up: sări la un răspuns mai vechi jump_reply_down: sări la un răspuns mai nou deleted: "Subiectul a fost șters" - auto_close_notice: "Acest subiect va fi automat închis în %{timeLeft}." - auto_close_notice_based_on_last_post: "Acest subiect va fi automat închis după %{duration} de la ultimul răspuns." + status_update_notice: + auto_close: "Acest subiect va fi automat închis în %{timeLeft}." + auto_close_based_on_last_post: "Acest subiect va fi automat închis după %{duration} de la ultimul răspuns." auto_close_title: 'Setări de închidere automată' - auto_close_save: "Salvează" - auto_close_remove: "Nu închide automat acest subiect" auto_close_immediate: one: "Ultima postare din subiect este deja veche de o oră, așa că subiectul va fi închis imediat." few: "Ultima postare din subiect este deja veche de %{count} ore, așa că subiectul va fi închis imediat." @@ -1387,7 +1387,6 @@ ro: '2_8': 'Vei primi notificări deoarece urmărești această categorie.' '2_4': 'Vei primi notificări deoarece ai postat un răspuns în acest subiect.' '2_2': 'Vei primi notificări fiindcă urmărești acest subiect.' - '2': 'Vei primi notificări fiindcă citești acest subiect.' '1_2': 'Vei fi notificat dacă cineva îți menționează @numele sau îți scrie un răspuns.' '1': 'Vei fi notificat dacă cineva îți menționează @numele sau îți scrie un răspuns.' '0_7': 'Ignori toate notificările din această categorie.' @@ -1423,7 +1422,6 @@ ro: open: "Deschide subiect" close: "Închide subiect" multi_select: "Selectează subiectele ..." - auto_close: "Închidere automată..." pin: "Fixează subiect..." unpin: "Anulează subiect fixat..." unarchive: "Dezarhivează subiect" @@ -2551,32 +2549,12 @@ ro: customize: title: "Personalizare" long_title: "Personalizările site-ului" - css: "CSS" - header: "Antet" - top: "Sus" - footer: "Subsol" - embedded_css: "Embedded CSS" - head_tag: - text: "" - title: "HTML care va fi inserat înaintea de tag-ul " - body_tag: - text: "" - title: "HTML care va fi inserat înaintea de tag-ul " - override_default: "Nu include foaia de stil standard" - enabled: "Activat?" preview: "previzualizare" - undo_preview: "șterge previzualizarea" - rescue_preview: "stil implicit" - explain_preview: "Vizualizează site-ul cu această foaie de stil personalizată" - explain_undo_preview: "Înapoi la foaia de stil personalizată activă acum" - explain_rescue_preview: "Vizualizează site-ul cu foaia de stil implicită" save: "Salvare" new: "Nou" new_style: "Stil nou" import: "Importă" - import_title: "Selectează un fișier sau lipește un text" delete: "Șterge" - delete_confirm: "Șterge această personalizare?" about: "Modifică foaia de stil CSS și header-ele HTML din site. Adaugă o personalizare pentru a începe." color: "Culoare" opacity: "Opacitate" @@ -2589,13 +2567,9 @@ ro: none_selected: "Selectează un șablon pentru a începe editarea" revert: "Revocă schimbările" revert_confirm: "Ești sigur că vrei să revoci schimbările?" - css_html: - title: "CSS/HTML" - long_title: "Personalizări CSS și HTML" colors: title: "Culori" long_title: "Scheme de culori" - about: "Modifică culorile folosite pe site fără a scrie CSS. Pentru a începe, adăugă o nouă schemă." new_name: "Nouă schemă de culori" copy_name_prefix: "Copie a" delete_confirm: "Ștergi această schemă de culori?" @@ -2734,8 +2708,6 @@ ro: change_trust_level: "schimbă nivelul de încredere" change_username: "schimbă numele utilizatorului" change_site_setting: "schimbă setările site-ului" - change_site_customization: "schimbă preferințele site-ului" - delete_site_customization: "șterge preferințele site-ului" change_site_text: "schimbă textul site-ului" suspend_user: "suspendă utilizatorul" unsuspend_user: "reactivează utilizator" @@ -2866,8 +2838,6 @@ ro: blocked: "Blocat?" staged: "În așteptare?" show_admin_profile: "Admin" - edit_title: "Editează titlu" - save_title: "Salvează titlu" refresh_browsers: "Forțează reîmprospătarea browserului" refresh_browsers_message: "Mesajul a fost trimis către toţi. " show_public_profile: "Arată profilul public" diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index ba7ad54674..52b5566d12 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -176,6 +176,8 @@ ru: emails_are_disabled: "Все исходящие письма были глобально отключены администратором. Уведомления любого вида не будут отправляться на почту." bootstrap_mode_enabled: "Чтобы облегчить развитие вашего нового сайта в самом начале, был включен режим запуска. В этом режиме, всем новым пользователям будет автоматически присвоен 1й уровень доверия при регистрации и включена ежедневная почтовая рассылка сводки новостей. Режим запуска будет выключен автоматически, как только количество зарегистрированных пользователей достигнет %{min_users}." bootstrap_mode_disabled: "Режим запуска будет отключен в течение 24 часов." + themes: + default_description: "По умолчанию" s3: regions: us_east_1: "US East (N. Virginia)" @@ -183,6 +185,7 @@ ru: us_west_2: "US West (Oregon)" us_gov_west_1: "AWS GovCloud (US)" eu_west_1: "EU (Ireland)" + eu_west_2: "EU (London)" eu_central_1: "EU (Frankfurt)" ap_southeast_1: "Asia Pacific (Singapore)" ap_southeast_2: "Asia Pacific (Sydney)" @@ -414,7 +417,6 @@ ru: automatic_group: Автоматическая Группа closed_group: Закрытая Группа is_group_user: "Вы участник этой группы" - allow_membership_requests: "Позволить пользователям проситься в группу владельцев (Необходима возможность упоминания группы всеми)" membership: "Участие" name: "Название" user_count: "Количество Участников" @@ -623,6 +625,7 @@ ru: revoke_access: "Лишить прав доступа" undo_revoke_access: "Отменить Лишение прав доступа" api_approved: "Подтверждено" + theme: "Стиль" staff_counters: flags_given: "полезные жалобы" flagged_posts: "сообщения с жалобами" @@ -998,22 +1001,27 @@ ru: password: "Пароль" email_placeholder: "E-mail или псевдоним" caps_lock_warning: "Caps Lock включен" - error: "Непредвиденная ошибка" - rate_limit: "Пожалуйста, сделайте перерыв перед очередной попыткой войти." + error: "Неизвестная ошибка" + rate_limit: "Сделайте перерыв перед очередной попыткой войти." blank_username_or_password: "Введите ваш e-mail (или псевдоним) и пароль." reset_password: 'Сброс пароля' logging_in: "Проверка..." or: "или" authenticating: "Проверка..." - awaiting_approval: "Ваша учетная запись еще не одобрена. Вы получите письмо, как только это случится." + awaiting_activation: "Ваша учетная запись ожидает активации через ссылку из письма. Чтобы повторно выслать активационное письмо, используйте кнопку сброса пароля." + awaiting_approval: "Ваша учетная запись еще не одобрена персоналом. Мы вышлем вам письмо, как только это произойдет." requires_invite: "К сожалению, доступ к этому форуму только по приглашениям." - not_activated: "Прежде, чем вы сможете войти на форум, вам необходимо активировать свою учетную запись. Мы отправили на почту {{sentTo}} подробные инструкции, как это cделать." + not_activated: "Прежде, чем вы сможете войти на форум, понадобиться активировать свою учетную запись. Мы отправили на почту {{sentTo}} подробные инструкции, как это cделать." not_allowed_from_ip_address: "С этого IP адреса вход запрещен." admin_not_allowed_from_ip_address: "Вы не можете войти в качестве администратора с этого IP адреса." - resend_activation_email: "Щелкните здесь, чтобы мы повторно выслали вам письмо для активации учетной записи." + resend_activation_email: "Щелкните здесь, чтобы повторно выслать письмо для активации учетной записи." + resend_title: "Заново выслать активационное письмо" + change_email: "Изменить электронную почту" + provide_new_email: "Укажите новый адрес электронной почты, чтобы задействовать его и заново выслать активационное письмо." + submit_new_email: "Обновить электронную почту" sent_activation_email_again: "По адресу {{currentEmail}} повторно отправлено письмо с инструкциями по активации вашей учетной записи. Доставка сообщения может занять несколько минут. Имейте в виду, что иногда по ошибке письмо может попасть в папку Спам." to_continue: "Пожалуйста, войдите" - preferences: "Вам необходимо войти на сайт для редактирования настроек пользователя" + preferences: "Необходимо войти на сайт для редактирования настроек профиля." forgot: "Я не помню данные моей учетной записи" not_approved: "Ваша учетная запись еще не прошла проверку. После успешной проверки мы отправим вам письмо с уведомлением, и вы сможете входить в свою учетную запись." google: @@ -1150,15 +1158,6 @@ ru: title: "Забыли указать получателей?" body: "В списке получателей сейчас только вы сами!" admin_options_title: "Дополнительные настройки темы для персонала" - auto_close: - label: "Закрыть тему через:" - error: "Пожалуйста, введите корректное значение." - based_on_last_post: "Не закрывать, пока не пройдет хотя бы такой промежуток времени с момента последнего сообщения в этой теме." - all: - examples: 'Введите количество часов (напр., 24), время (напр., 17:30) или дату и время (2013-11-22 14:00).' - limited: - units: "(кол-во часов)" - examples: 'Введите количество часов (24).' notifications: title: "уведомления об упоминании @псевдонима, ответах на ваши посты и темы, сообщения и т.д." none: "Уведомления не могут быть загружены." @@ -1428,11 +1427,7 @@ ru: jump_reply_up: перейти к более ранним ответам jump_reply_down: перейти к более поздним ответам deleted: "Тема удалена" - auto_close_notice: "Тема будет автоматически закрыта через %{timeLeft}." - auto_close_notice_based_on_last_post: "Эта тема будет закрыта через %{duration} после последнего ответа." auto_close_title: 'Настройки закрытия темы' - auto_close_save: "Сохранить" - auto_close_remove: "Не закрывать тему автоматически" auto_close_immediate: one: "Последнее сообщение в этой теме отправлено 1 час назад, поэтому данная тема будет закрыта незамедлительно." few: "Последнее сообщение в этой теме отправлено %{count} часа назад, поэтому данная тема будет закрыта незамедлительно." @@ -1467,7 +1462,6 @@ ru: '2_8': 'Вы будете получать уведомления, т.к. следите за этим разделом.' '2_4': 'Вы будете получать уведомления, т.к. ответили в теме.' '2_2': 'Вы будете получать уведомления, т.к. следите за этой темой.' - '2': 'Вы будете получать уведомления, т.к. читали эту тему.' '1_2': 'Вы будете получать уведомления, если кто-то упомянет ваш @псевдоним или ответит вам.' '1': 'Вы будете получать уведомления, если кто-то упомянет ваш @псевдоним или ответит вам.' '0_7': 'Не получать уведомлений из этого раздела.' @@ -1503,7 +1497,6 @@ ru: open: "Открыть тему" close: "Закрыть тему" multi_select: "Выбрать сообщения..." - auto_close: "Автоматическое закрытие..." pin: "Закрепить тему..." unpin: "Открепить тему..." unarchive: "Разархивировать тему" @@ -2722,32 +2715,14 @@ ru: customize: title: "Оформление" long_title: "Стили и заголовки" - css: "CSS" - header: "Заголовок" - top: "Топ" - footer: "нижний колонтитул" - embedded_css: "Встроенные CSS" - head_tag: - text: "" - title: "HTML код, который будет добавлен перед тегом ." - body_tag: - text: "" - title: "HTML код, который будет добавлен перед тегом ." - override_default: "Не использовать стандартную таблицу стилей" - enabled: "Разрешить?" preview: "как будет" - undo_preview: "удалить предпросмотр" - rescue_preview: "стиль по умолчанию" - explain_preview: "Посмотреть сайт с этой таблицей стилей" - explain_undo_preview: "Вернуться к текущей таблице стилей" - explain_rescue_preview: "Посмотреть сайт со стандартной таблицей стилей" + explain_preview: "Предпросмотр сайта с активированным стилем" save: "Сохранить" new: "Новое" new_style: "Новый стиль" import: "Импорт" - import_title: "Выберите файл или вставьте текст" delete: "Удалить" - delete_confirm: "Удалить настройки?" + delete_confirm: "Удалить этот стиль?" about: "Измените CSS стили и HTML заголовки на сайте. Чтобы начать, внесите правки." color: "Цвет" opacity: "Прозрачность" @@ -2760,13 +2735,70 @@ ru: none_selected: "Выберите шаблон письма, чтобы начать редактирование." revert: "Отменить изменения" revert_confirm: "Вы уверены, что хотите отменить Ваши изменения?" - css_html: - title: "CSS/HTML" - long_title: "Настройка CSS и HTML" + theme: + import_theme: "Импортировать стиль" + customize_desc: "Настроить:" + title: "Стили" + long_title: "Стилизация сайта: цвета, CSS и HTML" + edit: "Редактировать" + edit_confirm: "Это импортированный стиль. Изменения CSS/HTML потеряются после очередного обновления стиля." + common: "Общее" + desktop: "Настольный" + mobile: "Мобильный" + preview: "Предпросмотр" + is_default: "Активировать по умолчанию" + user_selectable: "Пользователи могут выбирать стиль" + color_scheme: "Цветовая схема" + color_scheme_select: "Выберите цвета для стиля" + custom_sections: "Настройка секций:" + included_themes: "Включенные стили" + child_themes_check: "Стиль включает дочерние стили" + css_html: "Настройка CSS/HTML" + edit_css_html: "Редактировать CSS/HTML" + edit_css_html_help: "Вы не внесли никаких изменений в CSS или HTML" + import_web_tip: "Репозиторий стиля" + import_file_tip: "файл стиля .dcstyle.json" + about_theme: "О стиле" + license: "Лицензия" + update_to_latest: "Обновить" + check_for_updates: "Проверить обновления" + updating: "Обновляю..." + up_to_date: "Стиль текущей версии, последняя проверка:" + add: "Добавить" + commits_behind: + one: "Есть 1 обновление стиля!" + few: "Есть {{count}} обновления стиля!" + many: "Есть {{count}} обновлений стиля!" + other: "Есть {{count}} обновлений стиля!" + scss: + text: "CSS" + title: "Введите CSS; допускаются все стили CSS и SCSS" + header: + text: "Шапка" + title: "Введите HTML для размещения над шапкой сайта" + after_header: + text: "Под шапкой" + title: "Введите HTML для размещения на всех страницах под шапкой" + footer: + text: "Подвал" + title: "Введите HTML для отображения в подвале сайта (внизу)" + embedded_scss: + text: "Встроенный CSS" + title: "Введите CSS для встраивания комментариев" + head_tag: + text: "" + title: "HTML для размещения перед тегом " + body_tag: + text: "" + title: "HTML для размещения перед тегом " colors: + select_base: + title: "Выберите базовую цветовую гамму" + description: "Базовая схема" title: "Цвета" + edit: "Редактировать цветовые схемы" long_title: "Цветовые схемы" - about: "Изменить цвета, используемые на этом сайте, без редактирования CSS. Добавьте новую схему для начала." + about: "Настройте цвета, используемые вашими стилями. Начните с создания новой цветовой схемы." new_name: "Новая цветовая схема" copy_name_prefix: "Копия" delete_confirm: "Удалить эту цветовую схему?" @@ -2905,8 +2937,8 @@ ru: change_trust_level: "изменен уровень доверия" change_username: "изменен псевдоним" change_site_setting: "изменена настройка сайта" - change_site_customization: "изменена настройка сайта" - delete_site_customization: "удалена настройка сайта" + change_theme: "Изменить стиль" + delete_theme: "Удалить стиль" change_site_text: "изменен текст" suspend_user: "пользователь заморожен" unsuspend_user: "пользователь разморожен" @@ -3049,8 +3081,6 @@ ru: blocked: "Заблокирован?" staged: "Имитация?" show_admin_profile: "Администратор" - edit_title: "Редактировать заголовок" - save_title: "Сохранить заголовок" refresh_browsers: "Выполнить перезагрузку браузера" refresh_browsers_message: "Сообщение отправлено всем клиентам!" show_public_profile: "Показать публичный профиль" @@ -3061,6 +3091,7 @@ ru: logged_out: "Пользователь вышел с сайта на всех устройствах" revoke_admin: 'Лишить прав Администратора' grant_admin: 'Выдать права Администратора' + grant_admin_confirm: "Мы отправили вам письмо с инструкциями для активации нового администратора." revoke_moderation: 'Лишить прав Модератора' grant_moderation: 'Выдать права Модератора' unblock: 'Разблокировать' diff --git a/config/locales/client.sk.yml b/config/locales/client.sk.yml index 8f0134243f..6588a2203f 100644 --- a/config/locales/client.sk.yml +++ b/config/locales/client.sk.yml @@ -1038,15 +1038,6 @@ sk: modal_cancel: "Zrušiť" cant_send_pm: "Ľutujeme, nemôžete poslať správu používateľovi %{username}." admin_options_title: "Nepovinné zamestnanecké nastavenia pre túto tému" - auto_close: - label: "Čas na automatické uzavretie témy:" - error: "Prosím zadajte platnú hodnotu." - based_on_last_post: "Nezatvárať pokým posledný príspevok v téme nie je takto starý." - all: - examples: 'Zadajte číslo v hodinách (24), absolútny čas (17:30) alebo časovú značku (2013-11-22 14:00).' - limited: - units: "(# hodín)" - examples: 'Zadajte počet hodín (24).' notifications: title: "oznámenia o zmienkach pomocou @meno, odpovede na Vaše príspevky a témy, správy, atď." none: "Notifikácie sa nepodarilo načítať" @@ -1277,11 +1268,7 @@ sk: jump_reply_up: prejsť na predchádzajúcu odpoveď jump_reply_down: prejsť na nasledujúcu odpoveď deleted: "Téma bola vymazaná" - auto_close_notice: "Táto téma bude automaticky uzavretá o %{timeLeft}." - auto_close_notice_based_on_last_post: "Táto téma bude uzavretá %{duration} po poslednej odpovedi." auto_close_title: 'Nastavenia automatického zatvárania' - auto_close_save: "Uložiť" - auto_close_remove: "Neuzatvárať túto tému automaticky" auto_close_immediate: one: "Posledný príspevok k téme je starý už 1 hodinu, takže téma bude okamžite uzavretá. " few: "Posledný príspevok k téme je starý už %{hours} hodiny, takže téma bude okamžite uzavretá. " @@ -1309,7 +1296,6 @@ sk: '2_8': 'Budete dostávať upozornenia, pretože pozorujete túto kategóriu.' '2_4': 'Budete dostávať upozornenia, pretože ste zaslali odpoveď na túto tému.' '2_2': 'Budete dostávať upozornenia, pretože pozorujete túto tému.' - '2': 'Budete dostávať upozornenia, pretože ste čítali túto tému.' '1_2': 'Budete upozornený ak niekto zmieni Vaše @meno alebo Vám odpovie.' '1': 'Budete upozornený ak niekto zmieni Vaše @meno alebo Vám odpovie.' '0_7': 'Ignorujete všetky upozornenia v tejto kategórii.' @@ -1345,7 +1331,6 @@ sk: open: "Otvoriť tému" close: "Uzavrieť tému" multi_select: "Označ príspevky...." - auto_close: "Automaticky zatvor...." pin: "Pripni tému...." unpin: "Odopni tému...." unarchive: "Zruš archiváciu témy" @@ -2333,32 +2318,12 @@ sk: customize: title: "Upraviť" long_title: "Úpravy webu" - css: "CSS" - header: "Hlavička" - top: "Vrch" - footer: "Päta" - embedded_css: "Vnorené CSS" - head_tag: - text: "" - title: "HTML, ktoré bude vložené pred tag" - body_tag: - text: "" - title: "HTML, ktoré bude vložené pred tag" - override_default: "Nevkladať štandardné štýly" - enabled: "Povolené?" preview: "náhľad" - undo_preview: "zmazať náhľad" - rescue_preview: "predvolený štýl" - explain_preview: "Nastaviť na stránke vlastné štýly" - explain_undo_preview: "Vrátiť sa k akruálnym vlastným štýlom" - explain_rescue_preview: "Nastavit na stránke štandardné štýly" save: "Uložiť" new: "Nový" new_style: "Nový štýl" import: "Import" - import_title: "Vyberte súbor alebo vložte text" delete: "Odstrániť" - delete_confirm: "Zmazať túto úpravu?" about: "Upraviť CSS štýly a HTML hlavičky na stránke. Začnite pridaním úpravy." color: "Farba" opacity: "Nepriesvitnosť" @@ -2371,13 +2336,9 @@ sk: none_selected: "Vyberte šablénu emailu pre začatie úpravy." revert: "Vrátiť zmeny" revert_confirm: "Ste si istý, že chcete vrátiť vykonané zmeny späť?" - css_html: - title: "CSS/HTML" - long_title: "Úpravy CSS a HTML" colors: title: "Farby" long_title: "Farebné schémy" - about: "Upravte farby použité na stránke bez použitia CSS. Začnite pridaním schémy." new_name: "Nová farebná schéma" copy_name_prefix: "Kópia" delete_confirm: "Zmazať túto farebnú schému?" @@ -2508,8 +2469,6 @@ sk: change_trust_level: "zmeniť stupeň dôvery" change_username: "zmeniť používateľské meno" change_site_setting: "zmeniť nastavenia webu" - change_site_customization: "zmeniť úpravy webu" - delete_site_customization: "zmazať úpravy webu" change_site_text: "zmeniť text stránky" suspend_user: "zruš práva používateľovi" unsuspend_user: "obnov práva používateľovi" @@ -2638,8 +2597,6 @@ sk: blocked: "Blokovaný?" staged: "Dočasný?" show_admin_profile: "Admin" - edit_title: "Upraviť názov" - save_title: "Uložiť názov" refresh_browsers: "Vynútiť refresh browsera." refresh_browsers_message: "Správa odoslaná všetkým klientom!" show_public_profile: "Ukázať verejný profil" diff --git a/config/locales/client.sq.yml b/config/locales/client.sq.yml index 93334e85a9..978c03dd71 100644 --- a/config/locales/client.sq.yml +++ b/config/locales/client.sq.yml @@ -992,15 +992,6 @@ sq: title: "Mos harruat të shtonit marrësit?" body: "Për momentin ky mesazh po ju dërgohet vetëm juve!" admin_options_title: "Rregullime opsionale të stafit për këtë temë" - auto_close: - label: "Data e mbylljes automatike të temës:" - error: "Ju lutem shkruani një vlerë të vlefshme." - based_on_last_post: "Mos e mbyll derisa postimi i fundit brenda temës të jetë të paktën kaq i vjetër. " - all: - examples: 'Vendosni numrin e orëve (24), orën absolute (17:30) ose një "timestamp" (2013-11-22 14:00).' - limited: - units: "(# i orëve)" - examples: 'Vendosni numrin e orëve (24).' notifications: title: "njoftimet për përmendjet @emri, përgjigjet ndaj postime dhe temave, mesazhet, etj." none: "Nuk i hapëm dot njoftimet." @@ -1202,11 +1193,7 @@ sq: jump_reply_up: hidhe tek përgjigja paraardhëse jump_reply_down: hidhu tek përgjigja pasardhëse deleted: "Tema është fshirë" - auto_close_notice: "Kjo temë do të mbyllet automatikisht %{timeLeft}." - auto_close_notice_based_on_last_post: "Kjo temë do të mbyllet %{duration} pas përgjigjes së fundit. " auto_close_title: 'Rregullimet e Mbylljes Automatike' - auto_close_save: "Ruaj" - auto_close_remove: "Mos e Mbyll Automatikisht Këtë Temë" timeline: back: "Kthehu mbrapa" back_description: "Kthehu mbrapa tek postimi i fundit i palexuar" @@ -1233,7 +1220,6 @@ sq: '2_8': 'Ju do të njoftoheni duke qënë se jeni duke gjurmuar këtë kategori. ' '2_4': 'Ju do të njoftoheni duke qënë se keni dërguar një përgjigje në këtë temë. ' '2_2': 'Ju do të njoftoheni duke qënë se jeni duke gjurmuar këtë temë. ' - '2': 'Ju do të merrni njoftime sepse e keni lexuar këtë temë.' '1_2': 'Ju do të njoftoheni nëse dikush përmend @emrin tuaj ose nëse dikush ju përgjigjet. ' '1': 'Ju do të njoftoheni nëse dikush përmend @emrin tuaj ose nëse dikush ju përgjigjet. ' '0_7': 'Ju nuk do të merrni asnjë njoftim për temat në këtë kategori. ' @@ -1269,7 +1255,6 @@ sq: open: "Hap temën" close: "Mbyll temën" multi_select: "Përzgjidhni Postimet..." - auto_close: "Mbylle Automatikisht..." pin: "Ngjite temën..." unpin: "Çngjite temën..." unarchive: "Çarkivoje temën" @@ -2182,31 +2167,12 @@ sq: customize: title: "Personalizo" long_title: "Personalizimet" - css: "CSS" - header: "Ballina" - top: "Popullore" - footer: "Fundi i faqes" - embedded_css: "CSS e ngjitur" - head_tag: - text: "" - title: "HTML që do të vendoset para mbylles së tagut " - body_tag: - text: "" - title: "HTML që do të vendoset para mbylljes së tagut " - override_default: "Mos përfshi faqen e stilit CSS standarte." - enabled: "Aktivizuar?" preview: "parashiko" - undo_preview: "hiqe parashikimin" - rescue_preview: "stili normal" - explain_preview: "Shife faqen me këtë faqe stili" - explain_undo_preview: "Rithehu tek faqja e stilit që është e aktivizuar tani" - explain_rescue_preview: "Shikoje faqen me stilin normal." save: "Ruaj" new: "E Re" new_style: "Veshje e Re" import: "Importo" delete: "Fshij" - delete_confirm: "Fshije këtë personalizim?" about: "Modifiko faqet e stilit CSS dhe HTML në këtë faqe. Shto një personalizim për të filluar. " color: "Ngjyra" opacity: "Opaciteti" @@ -2219,9 +2185,6 @@ sq: none_selected: "Zgjidhni një shabllon emaili për të filluar redaktimin. " revert: "Rikthe ndryshimet" revert_confirm: "A jeni i sigurtë që doni të riktheni ndryshimet?" - css_html: - title: "CSS/HTML" - long_title: "Personalizime CSS dhe HTML" colors: title: "Ngjyrat" long_title: "Skemat e ngjyrave" @@ -2341,8 +2304,6 @@ sq: change_trust_level: "ndrysho nivelin e besimit" change_username: "ndrysho emrin e përdoruesit" change_site_setting: "ndrysho parametrin e faqes" - change_site_customization: "ndrysho personalizimin e faqes" - delete_site_customization: "fshi personalizimin e faqes" change_site_text: "ndrysho tekstin e faqes" suspend_user: "pezullo anëtarin" unsuspend_user: "çpezullo anëtarin" @@ -2459,8 +2420,6 @@ sq: blocked: "Bllokuar?" staged: "Staged?" show_admin_profile: "Admin" - edit_title: "Redakto Titullin" - save_title: "Ruaj Titullin" refresh_browsers: "Forco rifreskimin e shfletuesit" refresh_browsers_message: "Mesazhi u dërgua tek të gjithë klientët!" show_public_profile: "Shfaq Profilin Publik" diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml index 940f2496f8..35d5d2db6a 100644 --- a/config/locales/client.sv.yml +++ b/config/locales/client.sv.yml @@ -135,6 +135,9 @@ sv: visible: enabled: 'listades %{when}' disabled: 'avlistades %{when}' + banner: + enabled: 'gjorde detta till en banderoll %{when}. Den kommer att visas högst upp på varje sida tills den blir avfärdad av användaren.' + disabled: 'tog bort denna banderoll %{when}. Den kommer inte längre att visas högst upp på varje sida.' topic_admin_menu: "administratörsåtgärder för ämne" emails_are_disabled: "All utgående e-post har blivit globalt inaktiverad av en administratör. Inga e-postnotifikationer av något slag kommer att skickas ut." bootstrap_mode_enabled: "Du är i bootstrap-läge för att göra lanseringen av din nya webbplats enklare. Alla nya användare kommer att beviljas förtroendenivå 1 och få dagliga sammanfattningar skickade via e-post. Det här stängs automatiskt av när det totala antalet användare överstiger %{min_users}." @@ -342,6 +345,7 @@ sv: title: 'Redigera grupp' full_name: 'Fullständigt namn' add_members: "Lägg till medlemmar" + delete_member_confirm: "Ta bort '%{username}' från gruppen '%{group}'?" request_membership_pm: title: "Begäran om medlemskap" body: "Jag skulle vilja ansöka om medlemskap i @%(namn på gruppen)." @@ -361,7 +365,7 @@ sv: automatic_group: Automatisk grupp closed_group: Stängd grupp is_group_user: "Du är en medlem av denna grupp" - allow_membership_requests: "Tillåt användare att sända medlemskaps förfrågan till gruppmedlemmar (Kräver att alla skall kunna nämna gruppen)" + allow_membership_requests: "Tillåt användare att skicka medlemskapsförfrågan till gruppägare" membership: "Medlemskap" name: "Namn" user_count: "Antal medlemmar" @@ -409,6 +413,7 @@ sv: description: "Du kommer aldrig att bli notifierad om något gällande nya ämnen i den här gruppen." flair_bg_color_placeholder: "Hex färgvärde" flair_color: "En Avatar som ändrar färg beroende på ljuset" + flair_color_placeholder: "(Valfritt) Hex-färgvärde" flair_preview_icon: "Förhandsvisa icon" flair_preview_image: "förhandsvisa bild" user_action_groups: @@ -554,6 +559,7 @@ sv: muted_users_instructions: "Undanta alla notiser från dessa användare." muted_topics_link: "Visa tystade ämnen" watched_topics_link: "Visa bevakade ämnen" + tracked_topics_link: "Visa bevakade ämnen" automatically_unpin_topics: "Avklistra automatiskt ämnen när jag når botten." apps: "Appar" revoke_access: "Återkalla åtkomst" @@ -739,8 +745,10 @@ sv: link_generated: "Länk för inbjudan framgångsrikt skapad!" valid_for: "Länk för inbjudan är endast giltig för denna email adress: %{email}" bulk_invite: + none: "Du har inte bjudit in någon ännu. Skicka individuella inbjudningar eller bjud in många personer på en gång genom att ladda upp en CSV-fil." text: "Massinbjudan från fil" success: "Filen laddades upp, du blir underrättad via meddelande när processen är klar" + error: "Tyvärr, filen bör vara i CSV-format." password: title: "Lösenord" too_short: "Ditt lösenord är för kort." @@ -748,6 +756,7 @@ sv: same_as_username: "Ditt lösenord är detsamma som ditt användarnamn." same_as_email: "Ditt lösenord är detsamma som din e-postadress." ok: "Ditt lösenord ser bra ut." + instructions: "åtminstone %{count} tecken" summary: title: "Sammanfattning" stats: "Statistik" @@ -918,12 +927,17 @@ sv: logging_in: "Loggar in..." or: "Eller" authenticating: "Autentiserar..." + awaiting_activation: "Ditt konto väntar på aktivering, använd glömt lösenord-länken för att skicka ett nytt aktiveringsmail." awaiting_approval: "Ditt konto har inte godkänts av en moderator än. Du kommer att få ett e-postmeddelande när det är godkänt." requires_invite: "Tyvärr, inbjudan krävs för tillgång till detta forum." not_activated: "Du kan inte logga in än. Vi har tidigare skickat ett aktiveringsbrev till dig via {{sentTo}}. Var god följ instruktionerna i det e-postmeddelandet för att aktivera ditt konto." not_allowed_from_ip_address: "Du kan inte logga in från den IP-adressen" admin_not_allowed_from_ip_address: "Du kan inte logga in som admin från den IP-adressen." resend_activation_email: "Klicka här för att skicka aktiveringsbrevet igen." + resend_title: "Skicka aktiveringsmail igen" + change_email: "Ändra mailadress" + provide_new_email: "Ange en ny adress så skickar vi ett nytt bekräftelsemail." + submit_new_email: "Ändra mailadress" sent_activation_email_again: "Vi har skickat ännu ett aktiveringsmail till dig via {{currentEmail}}. Det kan ta ett par minuter för det att komma fram; var noga med att kolla din skräppost." to_continue: "Var vänligen och logga in" preferences: "Du behöver logga in för att ändra dina användarpreferenser." @@ -953,6 +967,14 @@ sv: invites: accept_title: "Inbjudan" welcome_to: "Välkommen till %{site_name}!" + invited_by: "Du bjöds in av:" + social_login_available: "Du kommer också att kunna logga in med social inloggningar via denna mail." + your_email: "Mailadressen för ditt konto är %{email}." + accept_invite: "Acceptera inbjudan" + success: "Ditt konto har skapats och du är nu inloggad." + password_label: "Ange lösenord (valfritt)" + password_reset: + continue: "Fortsätt till %{site_name}" emoji_set: apple_international: "Apple/International" google: "Google" @@ -961,7 +983,7 @@ sv: win10: "Win10" category_page_style: categories_only: "Endast kategorier" - categories_with_featured_topics: "Kategorier med framhävda ämnen" + categories_with_featured_topics: "Kategorier med utvalda ämnen" categories_and_latest_topics: "Kategorier med senaste ämnen" shortcut_modifier_key: shift: 'Shift' @@ -987,6 +1009,7 @@ sv: other: "Genom att nämna {{group}}, så kommer du att notifiera {{count}} medlemmar – är du säker?" cannot_see_mention: category: "Du nämnde {{username}} men hen kommer inte få någon notifikation för hen har inte tillgång till denna kategori. Du behöver lägga till hen till en grupp som har tillgång till den här kategorin." + private: "Du nämnde {{username}} men hen kommer inte få någon notifikation eftersom hen inte kan se detta personliga meddelande. Du behöver bjuda in hen till detta PM." duplicate_link: "Det ser ut som att din länk till {{domain}} redan har delats i ämnet av @{{username}} i ett svar {{ago}} sedan – är du säker på att du vill dela den igen?" error: title_missing: "Du måste ange en rubrik" @@ -1053,15 +1076,6 @@ sv: title: "Glömde du lägga till mottagare?" body: "Just nu skickas det här meddelandet bara till dig själv!" admin_options_title: "Valfria personalinställningar för detta ämne" - auto_close: - label: "Stäng automatiskt ämnet efter:" - error: "Vänligen ange ett giltigt värde." - based_on_last_post: "Stäng inte förrän det sista inlägget i ämnet är åtminstone så här gammalt." - all: - examples: 'Ange antalet timmar (24), klockslag (17:30) eller tidstämpel (2013-11-22 14:00).' - limited: - units: "(# antal timmar)" - examples: 'Ange antal timmar (24).' notifications: title: "notiser från @namn-omnämnanden, svar på dina inlägg och ämnen, meddelanden, etc" none: "Kan inte ladda notiser just nu." @@ -1131,6 +1145,7 @@ sv: sort_by: "Sortera efter" relevance: "Relevans" latest_post: "Senaste inlägg" + latest_topic: "Senaste ämnet" most_viewed: "Mest sedda" most_liked: "Mest omtyckt" select_all: "Markera alla" @@ -1172,6 +1187,8 @@ sv: first: är den första posten pinned: är pinnade unpinned: är inte pinnade + seen: Jag har läst + unseen: Jag har inte läst wiki: är wiki statuses: label: Där ämnen @@ -1216,7 +1233,10 @@ sv: selected: one: "Du har markerat 1 ämne." other: "Du har markerat {{count}} ämnen." + change_tags: "Ersätt taggar" + append_tags: "Lägg till taggar" choose_new_tags: "Välj nya taggar för de här ämnena:" + choose_append_tags: "Välj nya taggar att lägga till för dessa ämnen:" changed_tags: "Taggarna för de här ämnena ändrades." none: unread: "Du har inga olästa ämnen." @@ -1304,11 +1324,36 @@ sv: jump_reply_up: hoppa till tidigare svar jump_reply_down: hoppa till senare svar deleted: "Ämnet har raderats" - auto_close_notice: "Det här ämnet kommer att stängas automatiskt %{timeLeft}." - auto_close_notice_based_on_last_post: "Ämnet stängs %{duration} efter sista svaret." + topic_status_update: + save: "Ställ in timer" + time: "Tid:" + remove: "Ta bort timer:" + publish_to: "Publicera till:" + auto_update_input: + limited: + units: "(antal timmar)" + examples: 'Ange antal timmar (24).' + all: + examples: 'Ange antal timmar (24), klockslag (17:30) eller tidsstämpel (2013-11-22 14:00).' + publish_to_category: + title: "Schemalägg publicering" + temp_open: + title: "Öppna tillfälligt" + auto_reopen: + title: "Öppna ämne automatiskt" + temp_close: + title: "Stäng tillfälligt" + auto_close: + title: "Stäng ämne automatiskt" + label: "Stäng automatiskt ämnet efter:" + error: "Vänligen ange ett giltigt värde." + based_on_last_post: "Stäng inte förrän det sista inlägget i ämnet är åtminstone så här gammalt." + status_update_notice: + auto_open: "Detta ämne kommer öppnas automatiskt om %{timeLeft}." + auto_close: "Det här ämnet kommer stängas automatiskt om %{timeLeft}." + auto_publish_to_category: "Detta ämne kommer att publiceras till #%{categoryName} %{timeLeft}." + auto_close_based_on_last_post: "Detta ämne stängs %{duration} efter sista svaret." auto_close_title: 'Inställningar för automatisk stängning' - auto_close_save: "Spara" - auto_close_remove: "Stäng inte det här ämnet automatiskt" auto_close_immediate: one: "Senaste inlägget i det här ämnet är redan 1 timme gammalt, så ämnet kommer att stängas omedelbart. " other: "Senaste inlägget i det här ämnet är redan %{count} timmar gammal, så ämnet kommer att stängas omedelbart. " @@ -1323,6 +1368,7 @@ sv: go: "gå" jump_bottom: "hoppa till sista inlägget" jump_prompt: "hoppa till" + jump_prompt_of: "av %{count} inlägg" jump_prompt_long: "Vilket inlägg vill du hoppa till?" jump_bottom_with_number: "hoppa till inlägg %{post_number}" total: antal inlägg @@ -1340,7 +1386,7 @@ sv: '2_8': 'Du kommer att få notifikationer eftersom du följer denna kategori.' '2_4': 'Du kommer att ta emot notifikationer för att du postade ett svar till detta ämne.' '2_2': 'Du kommer att ta emot notifikationer för att du följer detta ämne.' - '2': 'Du kommer att ta emot notifikationer för att du läser detta ämne.' + '2': 'Du kommer att ta emot notifikationer för att du läste detta ämne.' '1_2': 'Du kommer få en notifiering om någon nämner ditt @namn eller svarar på ditt inlägg.' '1': 'Du kommer få en notifiering om någon nämner ditt @namn eller svarar på ditt inlägg.' '0_7': 'Du ignorerar alla notifikationer i den här kategorin.' @@ -1376,7 +1422,6 @@ sv: open: "Öppna ämne" close: "Stäng ämne" multi_select: "Välj inlägg..." - auto_close: "Stäng automatiskt..." pin: "Klistra ämne..." unpin: "Avklistra ämne..." unarchive: "Dearkivera ämne" @@ -1556,6 +1601,7 @@ sv: file_too_large: "Tyvärr, filen är för stor (maximal filstorlek är {{max_size_kb}}kb). Varför inte ladda upp din stora fil till en moln-delningstjänst och sen dela länken?" too_many_uploads: "Tyvärr, du kan bara ladda upp en bild i taget." too_many_dragged_and_dropped_files: "Tyvärr, du kan bara ladda upp 10 filer åt gången." + upload_not_authorized: "Tyvärr, filen du försöker ladda upp är inte tillåten (tillåtna filtyper: %{authorized_extensions})." image_upload_not_allowed_for_new_user: "Tyvärr, nya användare kan inte ladda upp bilder." attachment_upload_not_allowed_for_new_user: "Tyvärr, nya användare kan inte bifoga filer." attachment_download_requires_login: "Tyvärr, du måste vara inloggad för att kunna ladda ned bifogade filer." @@ -1710,8 +1756,10 @@ sv: raw_email: displays: text_part: + title: "Visa text-delen av mailet" button: 'Text' html_part: + title: "Visa html-delen av mailet" button: 'HTML' category: can: 'can… ' @@ -1764,7 +1812,12 @@ sv: email_in_disabled: "Att skapa nya ämnen via e-post är avaktiverat i webbplatsinställningarna. För att aktivera ämnen skapade via e-post," email_in_disabled_click: 'aktivera "inkommande e-post" inställningen.' suppress_from_homepage: "Undanta den här kategorin från hemsidan." + show_subcategory_list: "Visa listan med underkategorier ovanför ämnen i denna kategori." + num_featured_topics: "Antal ämnen som visas på sidan kategorier:" all_topics_wiki: "Gör nya inlägg till wikis som standard" + subcategory_list_style: "Liststil på underkategori:" + sort_order: "Sortera ämneslista enligt:" + default_view: "Förvald ämneslista" allow_badges_label: "Tillåt utmärkelser i den här kategorin" edit_permissions: "Redigera behörigheter" add_permission: "Lägg till behörighet" @@ -1802,6 +1855,9 @@ sv: created: "Skapad" sort_ascending: 'Stigande' sort_descending: 'Fallande' + subcategory_list_styles: + rows: "Rader" + rows_with_featured_topics: "Rader med utvalda ämnen" flagging: title: 'Tack för att du hjälper till att hålla forumet civiliserat!' action: 'Flagga inlägg' @@ -1886,6 +1942,9 @@ sv: one: "visning" other: "visningar" replies: "Svar" + views_long: + one: "detta ämnet har visats 1 gång" + other: "detta ämne har visats {{number}} gånger" activity: "Aktivitet" likes: "Gillningar" likes_lowercase: @@ -1900,6 +1959,7 @@ sv: history: "Historik" changed_by: "av {{author}}" raw_email: + title: "Inkommande mail" not_available: "Ej tillgänglig!" categories_list: "Kategorilista" filters: @@ -2035,6 +2095,7 @@ sv: mark_regular: 'm, r Vanligt (standard) ämne' mark_tracking: 'm, t Följ ämne' mark_watching: 'm, w Bevaka ämne' + print: 'ctrl+p Skriv ut ämne' badges: earned_n_times: one: "Förtjänade den här utmärkelsen 1 gång" @@ -2301,6 +2362,8 @@ sv: add_owners: Lägg till ägare incoming_email: "Egenvald inkommande e-postadress" incoming_email_placeholder: "Ange e-postadress" + none_selected: "Välj en grupp för att komma igång" + no_custom_groups: "Skapa en egen grupp" api: generate_master: "Generera API-huvudnyckel" none: "Det finns inga aktiva API-nycklar just nu." @@ -2318,6 +2381,8 @@ sv: web_hooks: title: "Webhookar" none: "Det finns inga webhookar just nu." + instruction: "Webhooks låter Discourse meddela externa tjänster när specifika händelser sker på din webbsida. När webhook:en triggas, skickas en POST-begäran till de angivna URL:erna." + detailed_instruction: "En POST-begäran skickas till angiven URL när vald händelse sker." new: "Ny webhook" create: "Skapa" save: "Spara" @@ -2325,6 +2390,7 @@ sv: description: "Beskrivning" controls: "Kontroller" go_back: "Tillbaka till listan" + payload_url: "Försändelse-URL" payload_url_placeholder: "https://example.com/postreceive" secret_invalid: "Hemligheten får inte ha några blanka tecken." secret_too_short: "Hemligheten bör vara minst 12 tecken." @@ -2335,6 +2401,7 @@ sv: event_chooser: "Vilka event vill du ska utlösa den här webhooken?" wildcard_event: "Skicka mig allt." individual_event: "Välj enskilda event." + verify_certificate: "Kontrollera TLS-certifikatet för försändelse-URL:en" active: "Aktiva" active_notice: "Vi kommer att leverera detaljerna kring eventet när det händer." categories_filter_instructions: "Relevanta webhookar kommer endast att utlösas om eventet är relaterat med specifika kategorier. Lämna blank för att utlösa webhookar för alla kategorier. " @@ -2431,6 +2498,8 @@ sv: without_uploads: "Ja (inkludera inte filer)" download: label: "Ladda ner" + title: "Skicka mail med nedladdningslänk" + alert: "En länk för nedladdning av denna säkerhetskopia har skickats till dig via mail." destroy: title: "Ta bort säkerhetskopian" confirm: "Är du säker på att du vill förstöra denna säkerhetskopia?" @@ -2461,32 +2530,12 @@ sv: customize: title: "Anpassa" long_title: "Sidanpassningar" - css: "CSS" - header: "Sidhuvud" - top: "Toppen" - footer: "Sidfot" - embedded_css: "Inbäddad CSS" - head_tag: - text: "" - title: "HTML som kommer att sättas in före taggen" - body_tag: - text: "" - title: "HTML som kommer att sättas in före taggen" - override_default: "Inkludera inte standard-stilmallen" - enabled: "Aktiverad?" preview: "förhandsgranska" - undo_preview: "ta bort förhandsgranskning" - rescue_preview: "standard stil" - explain_preview: "Se sidan med skräddarsydd stilmall" - explain_undo_preview: "Gå tillbaka till den nuvarande aktiva stilmallen" - explain_rescue_preview: "Se sidan med standard-stilmallen" save: "Spara" new: "Ny" new_style: "Ny stil" import: "Importera" - import_title: "Välj en fil eller klistra in text" delete: "Radera" - delete_confirm: "Radera denna anpassning?" about: "Modifiera CSS stilmallar och HTML sidhuvuden på sidan. Lägg till en anpassning för att börja." color: "Färg" opacity: "Opacitet" @@ -2499,13 +2548,9 @@ sv: none_selected: "Välj en e-postmall för att börja redigera." revert: "Ångra ändringar" revert_confirm: "Är du säker på att du vill ångra dina ändringar?" - css_html: - title: "CSS/HTML" - long_title: "CSS- och HTML-anpassningar" colors: title: "Färger" long_title: "Färgscheman" - about: "Modifiera färgerna som används utan att skriva CSS. Lägg till ett schema för att börja." new_name: "Nytt färgschema" copy_name_prefix: "Kopia av" delete_confirm: "Ta bort det här färgschemat?" @@ -2644,8 +2689,6 @@ sv: change_trust_level: "ändra förtroendenivå" change_username: "ändra användarnamn" change_site_setting: "ändra sidinställning" - change_site_customization: "ändra webbplatsanpassning" - delete_site_customization: "radera webbplatsanpassning" change_site_text: "ändra webbplatsens text" suspend_user: "stäng av användare" unsuspend_user: "häv avstängning av användare" @@ -2780,17 +2823,17 @@ sv: blocked: "Blockerad?" staged: "Arrangerad?" show_admin_profile: "Administratör" - edit_title: "Redigera titel" - save_title: "Spara titel" refresh_browsers: "Tvinga webbläsaruppdatering" refresh_browsers_message: "Meddelande skickat till alla klienter!" show_public_profile: "Visa publik profil" impersonate: 'Imitera' + action_logs: "Åtgärdslogg" ip_lookup: "Kolla upp IP-adress" log_out: "Logga ut" logged_out: "Användaren loggades ut från alla enheter." revoke_admin: 'Återkalla Administratör' grant_admin: 'Bevilja Administratör' + grant_admin_confirm: "Vi har skickat ett mail till dig för att bekräfta den nya administratören. Vänligen öppna det och följ instruktionerna." revoke_moderation: 'Återkalla Moderering' grant_moderation: 'Bevilja Moderering' unblock: 'Avblockera' diff --git a/config/locales/client.te.yml b/config/locales/client.te.yml index cdf8337f99..95794fbe4e 100644 --- a/config/locales/client.te.yml +++ b/config/locales/client.te.yml @@ -584,15 +584,6 @@ te: help: "మార్క్ డైన్ సవరణ సహాయం" toggler: "దాచు లేదా చూపు కంపోజరు ఫలకం" admin_options_title: "ఈ విషయానికి ఐచ్చిక సిబ్బంది అమరికలు" - auto_close: - label: "విషయపు స్వీయ ముగింపు కాలం:" - error: "దయచేసి చెల్లే విలువ రాయండి" - based_on_last_post: "ఈ విషయంలో చివరి టపా కనీసం ఇంత వయసు వచ్చేంతవరకూ విషయాన్ని మూయకు." - all: - examples: 'గంటలు (24), సమయం(17:30) లేదా కాలముద్రణ (2013-11-22 14:00) రాయండి.' - limited: - units: "(# గంటలు)" - examples: 'గంటల సంఖ్య(24)ను రాయండి.' notifications: none: "ఈ సమయంలో ప్రకటనలు చూపలేకున్నాము." more: "పాత ప్రకటనలు చూడు" @@ -708,11 +699,7 @@ te: jump_reply_up: పాత జవాబుకు వెళ్లు jump_reply_down: తరువాతి జవాబుకు వెళ్లు deleted: "ఈ విషయం తొలగించబడింది" - auto_close_notice: "ఈ విషయం %{timeLeft} తర్వాత స్వీయంగా మూయబడుతుంది." - auto_close_notice_based_on_last_post: "చివరి జవాబు తర్వాత %{duration}కు ఈ విషయం స్వీయ మూయబడుతుంది" auto_close_title: 'స్వీయ ముగింపు అమరికలు' - auto_close_save: "దాచు" - auto_close_remove: "ఈ విషయాన్ని స్వీయ ముగించవద్దు" progress: title: విషయపు పురోగతి go_top: "అగ్ర" @@ -731,7 +718,6 @@ te: '2_8': 'మీకు ప్రకటనలు వస్తాయి ఎందుకంటే మీరు ఈ వర్గాన్ని గమనిస్తున్నారు.' '2_4': 'మీకు ప్రకటనలు వస్తాయి ఎందుకంటే మీరు ఈ విషయానికి జవాబిచ్చారు.' '2_2': 'మీకు ప్రకటనలు వస్తాయి ఎందుకంటే మీరు ఈ విషయాన్ని గమనిస్తున్నారు.' - '2': 'మీకు ప్రకటనలు వస్తాయి, ఎందుకంటే మీరు ఈ విషయాన్ని చదివారు.' '0_7': 'ఈ వర్గంలోని అన్ని ప్రకటనలనూ మీరు విస్మరిస్తున్నారు.' '0_2': 'ఈ విషయంలోని అన్ని ప్రకటనలనూ మీరు విస్మరిస్తున్నారు.' '0': 'ఈ విషయంలోని అన్ని ప్రకటనలనూ మీరు విస్మరిస్తున్నారు.' @@ -1306,40 +1292,18 @@ te: customize: title: "కస్టమైజ్" long_title: "సైట్ కస్టమైజేషనులు" - css: "సీయస్ యస్" - header: "హెడర్" - top: "అగ్ర" - footer: "ఫుటరు" - head_tag: - text: "" - title: " కొస ముందు ఉంచే హెచ్ టీ యం యల్" - body_tag: - text: "" - title: " కొస ముందు ఉంచే హెచ్ టీ యం యల్" - override_default: "స్టాండర్డ్ సైల్ షీట్ ఉంచకు" - enabled: "చేతమైందా?" preview: "మునుజూపు" - undo_preview: "మునుజూపు తొలగించు" - rescue_preview: "అప్రమేయ స్టైలు" - explain_preview: "సైటును అనురూప స్టైల్షీటుతో దర్శించు" - explain_undo_preview: "ప్రస్తుతం చేతనం చేసిఉన్న కస్టమ్ స్టైల్ షీటుకు మరలు" - explain_rescue_preview: "సైటును అప్రమేయ స్టైల్ షీటుతో చూడు" save: "భద్రపరుచు" new: "కొత్త" new_style: "కొత్త స్టైలు" delete: "తొలగించు" - delete_confirm: "ఈ కస్టమైజేషనులు తొలగించు? " about: "సైట్లో CSS స్టైల్‌షీట్స్ and HTML హెడర్స్ మార్చండి.స్టార్ట్‌కి కస్టమైజేషన్ కలపండి." color: "రంగు" opacity: "అపారదర్శకత" copy: "నకలు" - css_html: - title: "సీయస్ యస్ / హెచ్ టీ యం యల్" - long_title: "సీ యస్ యస్ మరియు హెచ్ టీ యం యల్ కస్టమైజేషనులు" colors: title: "రంగులు" long_title: "రంగు స్కీములు" - about: "సైట్లో వాడే రంగులు CSS వ్రాయకుండా మార్చండి.స్కీమ్ ను స్టార్ట్ కు కలపండి." new_name: "కొత్త రంగు స్కీము" copy_name_prefix: "దీనికి నకలు" delete_confirm: "ఈ రంగు స్కీము తొలగించు?" @@ -1447,8 +1411,6 @@ te: change_trust_level: "నమ్మకపు స్థాయి మార్చు" change_username: "సభ్యనామం మార్చు" change_site_setting: "సైటు అమరిక మార్చు" - change_site_customization: "సైట్ కస్టమైజేషను మార్చు" - delete_site_customization: "సైటు కస్టమైజేషను తొలగించు" suspend_user: "సభ్యుడిని సస్పెండు చేయి" unsuspend_user: "సస్పెండు కాని సభ్యుడు" grant_badge: "బ్యాడ్జ్ ఇవ్వు" @@ -1550,8 +1512,6 @@ te: admin: "అధికారి?" blocked: "నిలిపిన?" show_admin_profile: "అధికారి" - edit_title: "శీర్షిక సవరించు" - save_title: "శీర్షిక భద్రపరుచు" refresh_browsers: "బ్రౌజరు తాజాకరణ బలవంతంచేయి" refresh_browsers_message: "అన్ని క్లైంటులకు సందేశం పంపబడింది!" show_public_profile: "ప్రజా ప్రవర చూపు" diff --git a/config/locales/client.tr_TR.yml b/config/locales/client.tr_TR.yml index 839adf7032..3abd53a5d3 100644 --- a/config/locales/client.tr_TR.yml +++ b/config/locales/client.tr_TR.yml @@ -336,7 +336,6 @@ tr_TR: automatic_group: Otomatik Grup closed_group: Kapanmış Grup is_group_user: "Bu grubun bir üyesisiniz." - allow_membership_requests: "Kullanıcıların Grup başlarına üyelik istekleri göndermelerine izin ver ( Herkesin grupta birbirleri hakkında bahsetme özelliğinin açık olması gerekmektedir.)" membership: "Üyelik" name: "İsim" user_count: "Grup Sayısı" @@ -1025,15 +1024,6 @@ tr_TR: title: "Alıcıları eklemeyi unuttun mu?" body: "Bu ileti şu an sadece sana gönderiliyor!" admin_options_title: "Bu konu için isteğe bağlı görevli ayarları" - auto_close: - label: "Başlığı otomatik kapatma zamanı:" - error: "Lütfen geçerli bir değer giriniz." - based_on_last_post: "Başlıktaki son gönderi de en az bu kadar eskiyinceye kadar kapatmayın." - all: - examples: 'Saat sayısı (24), kesin zaman (17:30) ya da zaman damgası (2013-11-22 14:00) girin.' - limited: - units: "(saat sayısı)" - examples: 'Saat sayısını giriniz (24).' notifications: title: "@isim bahsedilişleri, gönderileriniz ve konularınıza verilen cevaplar, iletilerle vb. ilgili bildiriler" none: "Şu an için bildirimler yüklenemiyor." @@ -1265,11 +1255,7 @@ tr_TR: jump_reply_up: Daha önceki cevaba geç jump_reply_down: Daha sonraki cevaba geç deleted: "Konu silindi " - auto_close_notice: "Bu konu %{timeLeft} otomatik olarak kapanacak." - auto_close_notice_based_on_last_post: "Bu konu son cevaptan %{duration} sonra kapanacak." auto_close_title: 'Otomatik Kapatma Ayarları' - auto_close_save: "Kaydet" - auto_close_remove: "Bu Konuyu Otomatik Olarak Kapatma" auto_close_immediate: other: "Konudaki son gönderi zaten %{count} saat eski, bu yüzden konu hemen kapatılacak." timeline: @@ -1301,7 +1287,6 @@ tr_TR: '2_8': 'Be kategoriyi takip ettiğiniz için bildirimlerini alacaksınız.' '2_4': 'Bu konuya cevap yazdığınız için bildirimlerini alacaksınız.' '2_2': 'Bu konuyu takip ettiğiniz için bildirimlerini alacaksınız.' - '2': 'Bu konuyu okuduğunuz için bildirimlerini alacaksınız.' '1_2': 'Birisi @isminizden bahsederse ya da size cevap verirse bildirim alacaksınız.' '1': 'Birisi @isminizden bahsederse veya size cevap verirse bildirim alacaksınız.' '0_7': 'Bu kategoriye ait tüm bildirimleri görmezden geliyorsunuz.' @@ -1337,7 +1322,6 @@ tr_TR: open: "Konuyu Aç" close: "Konuyu Kapat" multi_select: "Gönderileri Seç..." - auto_close: "Otomatik Kapat..." pin: "Başa Tuttur..." unpin: "Baştan Kaldır..." unarchive: "Konuyu Arşivden Kaldır" @@ -2366,32 +2350,12 @@ tr_TR: customize: title: "Özelleştir" long_title: "Site Özelleştirmeleri" - css: "CSS" - header: "Başlık" - top: "En Kısım" - footer: "Alt Kısım" - embedded_css: "Gömülü CSS" - head_tag: - text: "" - title: " etiketinden önce eklenecek HTML" - body_tag: - text: "" - title: " etiketinden önce eklenecek HTML" - override_default: "Standart biçim sayfasını eklemeyin" - enabled: "Etkin mi?" preview: "önizleme" - undo_preview: "önizlemeyi kaldır" - rescue_preview: "öntanımlı biçim" - explain_preview: "Websitesine bu özelleştirilmiş biçim sayfası ile bak" - explain_undo_preview: "Şu an etkin olan özelleştirilmiş biçim sayfasına geri dön" - explain_rescue_preview: "Websitesine varsayılan biçim sayfası ile bak" save: "Kaydet" new: "Yeni" new_style: "Yeni Biçim" import: "İçeri Aktar" - import_title: "Bir dosya seçin ya da kopyalayıp yapıştırın" delete: "Sil" - delete_confirm: "Bu özelleştirmeyi sil?" about: "Sitedeki CSS biçim sayfalarını ve HTML başlıklarını değiştir. Özelleştirme ekleyerek başla." color: "Renk" opacity: "Opaklık" @@ -2404,13 +2368,9 @@ tr_TR: none_selected: "Düzenlemeye başlamak için bir e-posta şablonu seçin. " revert: "Değişiklikleri Geri Al" revert_confirm: "Değişikliklerinizi geri almak istediğinize emin misiniz?" - css_html: - title: "CSS/HTML" - long_title: "CSS ve HTML Özelleştirmeleri" colors: title: "Renkler" long_title: "Renk Düzenleri" - about: "Sitede kullanılan renkleri CSS yazmadan değiştir. Renk düzeni ekleyerek başla." new_name: "Yeni Renk Düzeni" copy_name_prefix: "Kopyası" delete_confirm: "Bu renk düzenini sil?" @@ -2549,8 +2509,6 @@ tr_TR: change_trust_level: "güven seviyesini değiştir" change_username: "kullanıcı adını değiştir" change_site_setting: "site ayarlarını değiştir" - change_site_customization: "websitesinin özelleştirmesini değiştir" - delete_site_customization: "websitesinin özelleştirmesini sil" change_site_text: "site metnini değiştir" suspend_user: "kullanıcıyı uzaklaştır" unsuspend_user: "kullanıcıyı uzaklaştırma" @@ -2681,8 +2639,6 @@ tr_TR: blocked: "Engellendi mi?" staged: "Aşamalı?" show_admin_profile: "Yönetici" - edit_title: "Başlığı Düzenle" - save_title: "Başlığı Kaydet" refresh_browsers: "Tarayıcıyı sayfa yenilemesine zorla" refresh_browsers_message: "İleti tüm kullanıcılara gönderildi!" show_public_profile: "Herkese Açık Profili Görüntüle" diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml index 309747afcf..b1b9d3cde0 100644 --- a/config/locales/client.uk.yml +++ b/config/locales/client.uk.yml @@ -687,9 +687,6 @@ uk: modal_ok: "OK" modal_cancel: "Скасувати" admin_options_title: "Необов'язкові налаштування персоналу для цієї теми" - auto_close: - limited: - units: "(# годин)" notifications: more: "переглянути старіші сповіщення" total_flagged: "всього дописів, на які поскаржилися" @@ -788,10 +785,7 @@ uk: jump_reply_up: перейти до ранішої відповіді jump_reply_down: перейти до пізнішої відповіді deleted: "Тему було видалено" - auto_close_notice: "Ця тема автоматично закриється %{timeLeft}." auto_close_title: 'Налаштування автоматичного закриття' - auto_close_save: "Зберегти" - auto_close_remove: "Не закривати цю тему автоматично" timeline: back: "Назад" progress: @@ -810,7 +804,6 @@ uk: '3': 'Ви будете отримувати сповіщення, бо Ви слідкуєте за цією темою.' '2_4': 'Ви будете отримувати сповіщення, бо Ви залишили відповідь в цій темі.' '2_2': 'Ви будете отримувати сповіщення, бо Ви стежите за цією темою.' - '2': 'Ви будете отримувати сповіщення, бо Ви читаєте цю тему.' '0_7': 'Ви ігноруєте всі сповіщення у цій категорії.' '0_2': 'Ви ігноруєте всі сповіщення з цієї теми.' '0': 'Ви ігноруєте всі сповіщення з цієї теми.' @@ -1323,18 +1316,12 @@ uk: customize: title: "Customize" long_title: "Site Customizations" - css: "CSS" - header: "Header" - override_default: "Do not include standard style sheet" - enabled: "Enabled?" preview: "preview" - rescue_preview: "стиль по замовчанню" save: "Save" new: "New" new_style: "New Style" import: "Імпорт" delete: "Delete" - delete_confirm: "Delete this customization?" color: "Колір" opacity: "Непрозорість" copy: "Копіювати" @@ -1344,13 +1331,9 @@ uk: body: "Тіло" revert: "Повернути зміни" revert_confirm: "Чи впевнені ви, що хочете повернути ваші зміни?" - css_html: - title: "CSS/HTML" - long_title: "Налаштування CSS та HTML" colors: title: "Кольори" long_title: "Схеми Кольорів" - about: "Змінюйте кольори, що використовуються на сайті без написання CSS. Додати схему, щоб почати. " new_name: "Нова Схема Кольорів" copy_name_prefix: "Копіювати з" delete_confirm: "Видалити цю кольорову схему?" @@ -1463,8 +1446,6 @@ uk: change_trust_level: "зміна рівня довіри" change_username: "зміна імені користувача" change_site_setting: "зміна налаштування сайта" - change_site_customization: "change site customization" - delete_site_customization: "delete site customization" change_site_text: "змінити текст сайту" suspend_user: "призупинення користувача" unsuspend_user: "скасування призупинення" @@ -1552,8 +1533,6 @@ uk: admin: "Адмін?" blocked: "Заблоковано?" show_admin_profile: "Адмін" - edit_title: "Редагувати назву" - save_title: "Зберегти назву" refresh_browsers: "Примусово оновити оглядач" refresh_browsers_message: "Повідомлення надіслано всім клієнтам!" show_public_profile: "Показати публічний профіль" diff --git a/config/locales/client.ur.yml b/config/locales/client.ur.yml index 732859f96e..d7f915c2fa 100644 --- a/config/locales/client.ur.yml +++ b/config/locales/client.ur.yml @@ -353,7 +353,6 @@ ur: automatic_group: خودکار گروپ closed_group: بند گروپ is_group_user: "آپ اس گروپ کے رکن ہیں" - allow_membership_requests: "صارفین کو گروپ کے مالکان کو ممبرشپ کی درخواست بھیجنے کی اجازت دیں (اِس کیلئے گروپ کا سب کی طرف سے قابلِ ذکرہونا ضروی ہے)" membership: "ممبرشپ " name: "نام" user_count: "ممبران کی تعداد" @@ -1061,15 +1060,6 @@ ur: title: "کیا آپ وصول کنندگان کو شامل کرنا بھول گئے؟" body: "اِس وقت یہ پیغام صرف اپنے آپ کو بھیجا جا رہا ہے!" admin_options_title: "اس ٹاپک کیلئے عملے کی اختیاری سیٹِنگ" - auto_close: - label: "خود کار طریقے سے ٹاپک بند کرنے کا وقت:" - error: "براہ کرم، ایک قابلِ قبول قدر درج کریں" - based_on_last_post: "بند نہ کریں جب تک ٹاپک کی آخری پوسٹ کم از کم اتنی پرانی نہ ہو۔" - all: - examples: 'گھنٹوں کی تعداد درج کریں (24)، مطلق وقت (17:30) یا ٹائمسٹیمپ (2013-11-22 14:00)۔' - limited: - units: "(گھنٹوں کے #)" - examples: 'گھنٹوں کی تعداد درج کریں (24)۔' notifications: title: "@نام کے ذکر، آپ کی پوسٹ اور ٹاپک پر جوابات، پیغامات، وغیرہ کی اطلاعات" none: "اِس وقت ویب سائٹ اطلاعات لوڈ کرنے سے قاصر ہے۔" @@ -1316,11 +1306,7 @@ ur: jump_reply_up: اِس سے پرانے جواب پر جائیں jump_reply_down: اِس سے نئے جواب پر جائیں deleted: "ٹاپک حذف کردیا گیا ہے" - auto_close_notice: "یہ ٹاپک خود کار طریقے سے بند کر دیا جائے گا ٪{timeLeft}۔" - auto_close_notice_based_on_last_post: "یہ ٹاپک آخری جواب کے ٪{مدت} بعد بند ہو جائے گا۔" auto_close_title: 'خود کار طریقے سے بند کر نے کی سیٹِنگ' - auto_close_save: "محفوظ کریں" - auto_close_remove: "اِس ٹاپک کو خود کار طریقے سے بند نہ کریں" auto_close_immediate: one: "ٹاپک کی آخری پوسٹ ابھی سے 1 گھنٹا پرانی ہے، اِسلیئے ٹاپک فوری طور پر بند کر دیا جائے گا۔" other: "ٹاپک کی آخری پوسٹ ابھی سے ٪{شمار} گھنٹے پرانی ہے، اِسلیئے ٹاپک فوری طور پر بند کر دیا جائے گا۔" @@ -1352,7 +1338,6 @@ ur: '2_8': 'آپ کو اطلاعات موصول ہوں گی کیونکہ آپ اِس زمرہ کو ٹرَیک کر رہے ہیں۔' '2_4': 'آپ کو اطلاعات موصول ہوں گی کیونکہ آپ نے اِس ٹاپک پر ایک جواب پوسٹ کیا۔' '2_2': 'آپ کو اطلاعات موصول ہوں گی کیونکہ آپ اِس ٹاپک کو ٹریک کر رہے ہیں۔' - '2': 'آپ کو اطلاعات موصول ہوں گی کیونکہ آپ نے یہ ٹاپک پڑھا ہے۔' '1_2': 'اگر کوئی آپ کا @نام زکر کرتا ہے یا کوئی جواب دیتا ہے تو آپ کو مطلع کر دیا جائے گا۔' '1': 'اگر کوئی آپ کا @نام زکر کرتا ہے یا کوئی جواب دیتا ہے تو آپ کو مطلع کر دیا جائے گا۔' '0_7': 'آپ اِس زمرہ میں تمام اطلاعات کو نظر انداز کر رہے ہیں۔' @@ -1388,7 +1373,6 @@ ur: open: "ٹاپک کھولیں" close: "ٹاپک بند کریں" multi_select: "پوسٹس منتخب کریں..." - auto_close: "خود کار طریقے سے بند کریں..." pin: "ٹاپک پِن کریں..." unpin: "ٹاپک سے پِن ہٹائیں..." unarchive: "ٹاپک آرکائیو سے ختم کریں" @@ -2504,32 +2488,12 @@ ur: customize: title: "مرضی کے مطابق بنائیں" long_title: "ویب سائٹ کو اپنی مرضی کے حساب سے بنانے کیلئے اختیارات" - css: "CSS" - header: "ہیڈر" - top: "ٹاپ" - footer: "فُوٹَر" - embedded_css: "اَیمبَیڈ کی ہوئی CSS" - head_tag: - text: "" - title: "HTML جو ٹیگ سے پہلے داخل کی جائے گی" - body_tag: - text: "" - title: "HTML جو ٹیگ سے پہلے داخل کی جائے گی" - override_default: "سٹینڈرڈ سٹائل شیٹ شامل نہ کریں" - enabled: "فعال؟" preview: "پیشگی دیکھیں" - undo_preview: "پیشگی وِیو ہٹائیں" - rescue_preview: "پہلے سے طے شدہ سٹائل" - explain_preview: "اپنی مرضی کی سٹائل شیٹ کے ساتھ سائٹ ملاحظہ کریں" - explain_undo_preview: "فی الحال فعال اپنی مرضی کی سٹائل شیٹ پر واپس جائیں" - explain_rescue_preview: "پہلے سے طے شدہ سٹائل شیٹ کے ساتھ سائٹ ملاحظہ کریں" save: "محفوظ کریں" new: "نیا" new_style: "نیا سٹائل" import: "اِمپورٹ" - import_title: "ایک فائل منتخب کریں یا ٹَیکسٹ پَیسٹ کریں" delete: "حذف کریں" - delete_confirm: "اس اِصلاح کو حذف کریں؟" about: "سائٹ پر CSS سٹائل شیٹس اور HTML ہیڈرز میں ترمیم کریں۔ شروع کرنے کیلئے ایک اِصلاح شامل کریں۔" color: "رنگ" opacity: "دھندلاپن" @@ -2542,13 +2506,9 @@ ur: none_selected: "ترمیم شروع کرنے کیلئے ایک اِیمیل ٹَیمپلیٹ منتخب کریں۔" revert: "تبدیلیاں لوٹائیں" revert_confirm: "کیا آپ واقعی تبدیلیاں لوٹانا چاہتے ہیں؟" - css_html: - title: "CSS/HTML" - long_title: "CSS اور HTML اصلاحات" colors: title: "رنگ" long_title: "رنگ سکیمیں" - about: "CSS لکھے بغیر ویب سائٹ پر استعمال رنگوں میں ترمیم کریں۔ شروع کرنے کے لئے ایک نئی سکیم بنائیں۔" new_name: "نئی رنگ سکیم" copy_name_prefix: "نقل از" delete_confirm: "اِس رنگ سکیم کو حذف کریں؟" @@ -2687,8 +2647,6 @@ ur: change_trust_level: "ٹرسٹ لَیول تبدیل کریں" change_username: "صارف نام تبدیل کریں" change_site_setting: "ویب سائٹ کی سیٹِنگ تبدیل کریں" - change_site_customization: "سائٹ اصلاحات تبدیل کریں" - delete_site_customization: "سائٹ اصلاحات حذف کریں" change_site_text: "سائٹ ٹَیکسٹ تبدیل کریں" suspend_user: "صارف معطل کریں" unsuspend_user: "صارف کی معطلی ختم کریں" @@ -2823,8 +2781,6 @@ ur: blocked: "بلاک کردہ؟" staged: "سٹَیجڈ؟" show_admin_profile: "ایڈمن" - edit_title: "عنوان میں ترمیم کریں" - save_title: "عنوان محفوظ کریں" refresh_browsers: "براؤزر رِیفریش فورس کریں" refresh_browsers_message: "تمام کلائینٹس کو پیغام بھیجا!" show_public_profile: "پبلک پروفائل دکھائیں" diff --git a/config/locales/client.vi.yml b/config/locales/client.vi.yml index de0028d8ac..9c9a8dbe61 100644 --- a/config/locales/client.vi.yml +++ b/config/locales/client.vi.yml @@ -118,9 +118,12 @@ vi: enabled: 'được hiển thị lúc %{when}' disabled: 'bỏ hiển thị lúc %{when}' topic_admin_menu: "Lựa chọn quản lí chủ đề." + wizard_required: "Chào mừng bạn đến với Discourse! Hãy bắt đầu với hướng dẫn cài đặt ✨" emails_are_disabled: "Ban quản trị đã tắt mọi email gửi đi. Sẽ không có bất kỳ thông báo nào qua email được gửi đi." bootstrap_mode_enabled: "Để đơn giản hoá quá trình triển khai, bạn đang ở trong chế độ bootstrap. Mọi người dùng mới đều có trust level 1 và sẽ nhận được email cập nhật thông tin mỗi ngày. Chế độ này sẽ tự động tắt khi số người dùng vượt qua %{min_users}." bootstrap_mode_disabled: "Chế độ bootstrap sẽ bị tắt trong 24 giờ tới." + themes: + default_description: "Mặc định" s3: regions: us_east_1: "US East (N. Virginia)" @@ -336,7 +339,6 @@ vi: automatic_group: Nhóm tự động closed_group: Nhóm riêng is_group_user: "Bạn là một thành viên của nhóm này" - allow_membership_requests: "Cho phép người dùng gửi thư xin gia nhập cho người đứng đầu nhóm (Yêu cầu mọi người phải có quyền nhắc tới nhóm)" membership: "Thành viên" name: "Tên" user_count: "Số lượng thành viên" @@ -512,7 +514,9 @@ vi: watched_categories: "Xem" tracked_categories: "Theo dõi" watched_first_post_categories: "Xem bài viết đầu tiên" + watched_first_post_categories_instructions: "Bạn sẽ nhận được thông báo khi có ai đó đăng chủ đề mới trong thư mục này." watched_first_post_tags: "Xem bài viết đầu tiên" + watched_first_post_tags_instructions: "Bạn sẽ nhận được thông báo khi có ai đó đăng chủ đề mới có chứa thẻ này." muted_categories: "Im lặng" muted_categories_instructions: "Bạn sẽ không bao giờ được thông báo về bất cứ điều gì về các chủ đề mới trong các chuyên mục này, và chúng sẽ không hiển thị mới nhất" delete_account: "Xoá Tài khoản của tôi" @@ -531,6 +535,7 @@ vi: revoke_access: "Lấy lại quyền" undo_revoke_access: "Cấp lại quyền" api_approved: "Chấp thuận:" + theme: "Giao diện" staff_counters: flags_given: "cờ hữu ích" flagged_posts: "bài viết gắn cờ" @@ -554,11 +559,14 @@ vi: error: "(lỗi)" action: "Gửi lại mật khẩu tới email" set_password: "Nhập Mật khẩu" + choose_new: "Chọn một mật khẩu mới" + choose: "Chọn một mật khẩu" change_about: title: "Thay đổi thông tin về tôi" error: "Có lỗi xảy ra khi thay đổi giá trị này." change_username: title: "Thay Username" + confirm: "Nếu bạn thay đổi tên đăng nhập, tất cả những liên kết người khác nhắc đến bạn @name sẽ bị lỗi. Bạn chắc chắn muốn thay đổi chứ?" taken: "Xin lỗi, đã có username này." error: "Có lỗi trong khi thay đổi username của bạn." invalid: "Username này không thích hợp. Nó chỉ chứa các ký tự là chữ cái và chữ số. " @@ -606,6 +614,7 @@ vi: short_instructions: "Mọi người có thể nhắc tới bạn bằng @{{username}}" available: "Tên đăng nhập của bạn có sẵn" not_available: "Chưa có sẵn. Thử {{suggestion}}?" + not_available_no_suggestion: "Không sẵn có" too_short: "Tên đăng nhập của bạn quá ngắn" too_long: "Tên đăng nhập của bạn quá dài" checking: "Đang kiểm tra username sẵn sàng để sử dụng...." @@ -639,12 +648,14 @@ vi: always: "luôn luôn" never: "không" email_digests: + title: "Khi tôi không ghé thăm, hãy gửi cho tôi email tóm tắt về các chủ đề nhiều người quan tâm nhất và các câu trả lời." every_30_minutes: "mỗi 30 phút" every_hour: "hàng giờ" daily: "hàng ngày" every_three_days: "ba ngày một" weekly: "hàng tuần" every_two_weeks: "hai tuần một" + include_tl0_in_digests: "Bao gồm nội dung của những thành viên mới trong email tóm tắt." email_in_reply_to: "Kèm theo đoạn dẫn trích trả lời bài viết trong email" email_direct: "Gửi cho tôi một email khi có người trích dẫn, trả lời cho bài viết của tôi, đề cập đến @username của tôi, hoặc mời tôi đến một chủ đề" email_private_messages: "Gửi cho tôi email khi có ai đó nhắn tin cho tôi" @@ -890,12 +901,23 @@ vi: github: title: "với GitHub" message: "Chứng thực với GitHub (chắc chắn chặn popup không bật)" + invites: + accept_title: "Lời mời" + welcome_to: "Chào mừng bạn đến với %{site_name}!" + invited_by: "Bạn đã được mời bởi:" + your_email: "Địa chỉ email của bạn là %{email}." + accept_invite: "Chấp nhận lời mời" + password_label: "Thiết lập mật khẩu (Không bắt buộc)" + password_reset: + continue: "Tiếp tục truy cập %{site_name}" emoji_set: google: "Google" twitter: "Twitter" win10: "Win10" category_page_style: categories_only: "Chỉ chuyên mục" + categories_with_featured_topics: "Các chuyên mục và chủ đề nổi bật" + categories_and_latest_topics: "Các chuyên mục và chủ đề mới" shortcut_modifier_key: shift: 'Shift' ctrl: 'Ctrl' @@ -975,15 +997,6 @@ vi: yourself_confirm: title: "Bạn có quên chưa thêm người nhận?" admin_options_title: "Tùy chọn quản trị viên cho chủ đề này" - auto_close: - label: "Thời gian tự khóa chủ đề:" - error: "Vui lòng nhập một giá trị hợp lệ." - based_on_last_post: "Không đóng cho đến khi bài viết cuối cùng trong chủ đề này trở thành bài cũ" - all: - examples: 'Nhập giờ (định dạng 24h), thời gian chính xác ( vd: 17:30) hoặc thời gian kèm ngày tháng (2013-11-22 14:00).' - limited: - units: "(# của giờ)" - examples: 'Nhập số giờ ( theo định dạng 24h)' notifications: title: "thông báo của @name nhắc đến, trả lời bài của bạn và chủ đề, tin nhắn, vv" none: "Không thể tải các thông báo tại thời điểm này." @@ -1049,6 +1062,7 @@ vi: sort_by: "Sắp xếp theo" relevance: "Độ phù hợp" latest_post: "Bài viết mới nhất" + latest_topic: "Chủ đề mới" most_viewed: "Xem nhiều nhất" most_liked: "Like nhiều nhất" select_all: "Chọn tất cả" @@ -1202,11 +1216,7 @@ vi: jump_reply_up: nhảy đến những trả lời trước đó jump_reply_down: nhảy tới những trả lời sau đó deleted: "Chủ đề này đã bị xóa" - auto_close_notice: "Chủ đề này sẽ tự động đóng %{timeLeft}." - auto_close_notice_based_on_last_post: "Chủ đề này sẽ đóng %{duration} sau trả lời cuối cùng." auto_close_title: 'Tự động-Đóng các Cài đặt' - auto_close_save: "Lưu" - auto_close_remove: "Đừng Tự Động-Đóng Chủ Đề Này" timeline: back: "Quay lại" progress: @@ -1230,7 +1240,6 @@ vi: '2_8': 'Bạn sẽ nhận được thông báo bởi vì bạn đang theo dõi chuyên mục này.' '2_4': 'Bạn sẽ nhận được các thông báo bởi vì bạn đã đăng một trả lời vào chủ đề này' '2_2': 'Bạn sẽ nhận được các thông báo bởi vì bạn đang theo dõi chủ đề này.' - '2': 'Bạn sẽ nhận được các thông báo bởi vì bạn đọc chủ đề này ' '1_2': 'Bạn sẽ được thông báo nếu ai đó đề cập đến @tên bạn hoặc trả lời bạn' '1': 'Bạn sẽ được thông báo nếu ai đó đề cập đến @tên bạn hoặc trả lời bạn' '0_7': 'Bạn đang bỏ qua tất cả các thông báo trong chuyên mục này' @@ -1266,7 +1275,6 @@ vi: open: "Mở Chủ Đề" close: "Đóng Chủ Đề" multi_select: "Chọn Bài Viết..." - auto_close: "Tự Động Đóng..." pin: "Ghim Chủ Đề..." unpin: "Bỏ-Ghim Chủ Đề..." unarchive: "Chủ đề Không Lưu Trữ" @@ -2085,32 +2093,12 @@ vi: customize: title: "Tùy biến" long_title: "Tùy biến trang" - css: "CSS" - header: "Header" - top: "Trên" - footer: "Footer" - embedded_css: "Nhúng CSS" - head_tag: - text: "" - title: "HTML sẻ thêm trước thẻ " - body_tag: - text: "" - title: "HTML sẽ thêm trước thẻ " - override_default: "Không bao gồm style sheet chuẩn" - enabled: "Cho phép?" preview: "xem trước" - undo_preview: "xóa xem trước" - rescue_preview: "default style" - explain_preview: "Xem website với stylesheet tùy chỉnh" - explain_undo_preview: "Quay trở lại với kiểu tùy chỉnh stylesheet hiện tại" - explain_rescue_preview: "Xem website với stylesheet mặc định" save: "Lưu" new: "Mới" new_style: "Style mới" import: "Nhập" - import_title: "Chọn một file hoặc paste chữ." delete: "Xóa" - delete_confirm: "Xóa tùy biến này?" about: "Chỉnh sửa CSS và HTML header trên trang. Thêm tùy biến để bắt đầu." color: "Màu sắc" opacity: "Độ mờ" @@ -2123,13 +2111,9 @@ vi: none_selected: "Chọn email template để bắt đầu chỉnh sửa." revert: "Hoàn nguyên thay đổi" revert_confirm: "Bạn có chắc chắn muốn hoàn nguyên các thay đổi?" - css_html: - title: "CSS/HTML" - long_title: "Tùy biến CSS và HTML" colors: title: "Màu sắc" long_title: "Bảng màu" - about: "Chỉnh " new_name: "Bản màu mới" copy_name_prefix: "Bản sao của" delete_confirm: "Xóa bảng màu này?" @@ -2263,8 +2247,6 @@ vi: change_trust_level: "thay đổi cấp tin cậy" change_username: "thay đổi username" change_site_setting: "thay đổi cấu hình trang" - change_site_customization: "thay đổi tùy biến trang" - delete_site_customization: "xóa tùy biến trang" change_site_text: "thay đổi chữ trên website" suspend_user: "tạm khóa thành viên" unsuspend_user: "hủy tạm khóa thành viên" @@ -2383,8 +2365,6 @@ vi: blocked: "Đã khóa?" staged: "Cấp bậc?" show_admin_profile: "Quản trị" - edit_title: "Sửa Tiêu đề" - save_title: "Lưu Tiêu đề" refresh_browsers: "Bắt buộc làm mới trình duyệt" refresh_browsers_message: "Tin nhắn đã gửi cho tất cả người dùng!" show_public_profile: "Hiển thị hồ sơ công khai" diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index 75c3b76941..d567f23fdc 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -336,7 +336,6 @@ zh_CN: automatic_group: 自动群组 closed_group: 封闭群组 is_group_user: "你是该群组的一个成员" - allow_membership_requests: "允许用户向群组拥有者发送加入小组请求(群组需要能让所有人提及)" membership: "成员资格" name: "名字" user_count: "成员数目" @@ -610,7 +609,7 @@ zh_CN: ok: "昵称可用" username: title: "用户名" - instructions: "不重复,无空格,短" + instructions: "独一无二,没有空格,简短" short_instructions: "其他人可以用 @{{username}} 来提及你" available: "用户名可用" not_available: "不可用。试试 {{suggestion}} ?" @@ -908,7 +907,7 @@ zh_CN: title: "Twitter 登录" message: "正在通过 Twitter 帐号验证登录(请确保浏览器没有禁止弹出窗口)" instagram: - title: "用 Instagram 登录" + title: "Instagram 登录" message: "正在通过 Instagram 帐号验证登录(请确保浏览器没有禁止弹出窗口)" facebook: title: "Facebook 登录" @@ -917,7 +916,7 @@ zh_CN: title: "Yahoo 登录" message: "正在通过 Yahoo 帐号验证登录(请确保浏览器没有禁止弹出窗口)" github: - title: "用 GitHub 帐号登录" + title: "GitHub 登录" message: "正在通过 GitHub 帐号验证登录(请确保浏览器没有禁止弹出窗口)" invites: accept_title: "邀请" @@ -1030,15 +1029,6 @@ zh_CN: title: "你忘记添加收信人了吗?" body: "目前该私信只发给了你自己!" admin_options_title: "本主题可选设置" - auto_close: - label: "自动关闭主题时间:" - error: "请输入一个有效值。" - based_on_last_post: "以最后回复的时间开始计时" - all: - examples: '输入小时数(24),绝对时间(17:30)或时间戳(2013-11-22 14:00)。' - limited: - units: "(# 小时数)" - examples: '输入小时数(24)。' notifications: title: "使用@提到你,回复你的内容、消息以及其他的通知" none: "现在无法载入通知" @@ -1273,11 +1263,7 @@ zh_CN: jump_reply_up: 转到更早的回复 jump_reply_down: 转到更新的回复 deleted: "此主题已被删除" - auto_close_notice: "本主题将在%{timeLeft}自动关闭。" - auto_close_notice_based_on_last_post: "在最后一个帖子 %{duration} 无人回复关闭帖子的时间。" auto_close_title: '自动关闭设置' - auto_close_save: "保存" - auto_close_remove: "不要自动关闭该主题" auto_close_immediate: other: "主题中的最后一帖是 %{hours} 小时前发出的,所以主题将会立即关闭。" timeline: @@ -1309,7 +1295,6 @@ zh_CN: '2_8': '因为你正在跟踪该分类,你将收到通知。' '2_4': '因为你发布了对该主题的回复,你将会收到通知。' '2_2': '因为你正在跟踪该主题,你将收到通知。' - '2': '因为你阅读了该主题,你将会收到通知。' '1_2': '如果有人@你或回复你,将通知你。' '1': '如果有人@你或回复你,将通知你。' '0_7': '你将忽略关于该分类的所有通知。' @@ -1345,7 +1330,6 @@ zh_CN: open: "打开主题" close: "关闭主题" multi_select: "选择帖子..." - auto_close: "自动关闭..." pin: "置顶主题..." unpin: "取消置顶主题..." unarchive: "取消存档主题" @@ -2375,32 +2359,12 @@ zh_CN: customize: title: "定制" long_title: "站点定制" - css: "CSS" - header: "头部" - top: "顶部" - footer: "底部" - embedded_css: "嵌入的 CSS" - head_tag: - text: "" - title: "将在 标签前插入的 HTML" - body_tag: - text: "" - title: "将在 标签前插入的 HTML" - override_default: "覆盖缺省值?" - enabled: "启用?" preview: "预览" - undo_preview: "移除预览" - rescue_preview: "默认样式" - explain_preview: "以自定义样式浏览此网页" - explain_undo_preview: "返回目前使用中的自定义样式" - explain_rescue_preview: "以默认样式浏览此网页" save: "保存" new: "新建" new_style: "新样式" import: "导入" - import_title: "选择一个文件或者粘贴文本" delete: "删除" - delete_confirm: "删除本定制内容?" about: "修改站点的 CSS 样式表和 HTML 头部。添加一个自定义方案开始。" color: "颜色" opacity: "透明度" @@ -2413,13 +2377,9 @@ zh_CN: none_selected: "选择一个邮件模板开始编辑。" revert: "撤销更变" revert_confirm: "你确定要撤销你的更变吗?" - css_html: - title: "CSS/HTML" - long_title: "自定义 CSS 和 HTML" colors: title: "颜色" long_title: "颜色方案" - about: "颜色方案让你能够让你在不写 CSS 的情况下更改色彩。添加一种颜色以开始。" new_name: "新的颜色方案" copy_name_prefix: "复制于" delete_confirm: "删除这个颜色方案?" @@ -2558,8 +2518,6 @@ zh_CN: change_trust_level: "更改信任等级" change_username: "修改用户名" change_site_setting: "更改站点设置" - change_site_customization: "更改站点自定义" - delete_site_customization: "删除站点自定义" change_site_text: "更改站点文字" suspend_user: "封禁用户" unsuspend_user: "解禁用户" @@ -2690,8 +2648,6 @@ zh_CN: blocked: "已封?" staged: "暂存?" show_admin_profile: "管理员" - edit_title: "编辑头衔" - save_title: "保存头衔" refresh_browsers: "强制浏览器刷新" refresh_browsers_message: "消息发送至所有用户!" show_public_profile: "显示公开介绍" diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index 51638d5626..a3a326b2ad 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -336,7 +336,6 @@ zh_TW: automatic_group: 自動群組 closed_group: 關閉群組 is_group_user: "你是團隊裡面的成員" - allow_membership_requests: "允許用戶向群組擁有者發送加入小組請求(群組需要能讓所有人提及)" membership: "會員資格" name: "名字" user_count: "成員數量" @@ -1030,15 +1029,6 @@ zh_TW: title: "你忘記添加收信人了嗎?" body: "目前該私信只發給了你自己!" admin_options_title: "此討論話題可選用之工作人員設定選項" - auto_close: - label: "自動關閉主題時間:" - error: "請輸入一個有效值。" - based_on_last_post: "在最後一個文章發表後,暫不關閉主題的時間。" - all: - examples: '輸入小時數(24)、絕對時間(17:30)、或時間戳(2013-11-22 14:00)。' - limited: - units: "(# 小時數)" - examples: '輸入小時數(24)' notifications: title: "當有人以「@用戶名稱」提及您、回覆您的貼文、或是傳送訊息給您的時候通知您的設定。" none: "目前無法載入通知訊息。" @@ -1273,11 +1263,7 @@ zh_TW: jump_reply_up: jump to earlier reply jump_reply_down: jump to later reply deleted: "此討論話題已被刪除" - auto_close_notice: "此討論話題將在 %{timeLeft}自動關閉。" - auto_close_notice_based_on_last_post: "主題在最後一則回覆後,將會關閉 %{duration}" auto_close_title: '自動關閉設定' - auto_close_save: "儲存" - auto_close_remove: "不要自動關閉此討論話題" auto_close_immediate: other: "主題中的最後一帖是 %{hours} 小時前發出的,所以主題將會立即關閉。" timeline: @@ -1309,7 +1295,6 @@ zh_TW: '2_8': '你將會收到通知,因為你在追蹤此分類。' '2_4': '你將收到關於此討論話題的通知,因為你回覆了此討論話題。' '2_2': '你將收到關於此討論話題的通知,因為你正在追蹤此討論話題。' - '2': '你將收到關於此討論話題的通知,因為你看過此討論話題。' '1_2': '如果有人@你或回覆你,將通知你。' '1': '如果有人@你或回覆你,將通知你。' '0_7': '你正忽略此分類中的所有通知。' @@ -1345,7 +1330,6 @@ zh_TW: open: "開放討論話題" close: "關閉討論話題" multi_select: "選擇文章" - auto_close: "自動關閉" pin: "置頂主題" unpin: "取消置頂主題" unarchive: "復原已封存的討論話題" @@ -2376,32 +2360,12 @@ zh_TW: customize: title: "客製化" long_title: "網站客製化" - css: "CSS" - header: "標頭" - top: "精選" - footer: "頁尾" - embedded_css: "內嵌 CSS" - head_tag: - text: "" - title: "HTML 將會置於 之前" - body_tag: - text: "" - title: "HTML 將會置於 之前" - override_default: "不要保含標準樣式" - enabled: "已啟用?" preview: "預覽" - undo_preview: "移除預覽" - rescue_preview: "預設風格" - explain_preview: "以此自訂樣式預覽網頁" - explain_undo_preview: "還原現時的自訂樣式" - explain_rescue_preview: "以預設樣式預覽網頁" save: "儲存" new: "新增" new_style: "新增樣式" import: "匯入" - import_title: "選取檔案或貼上文本" delete: "刪除" - delete_confirm: "刪除此樣式?" about: "修改網站的 CSS 和 HTML headers。請新增一個自定樣式來開始使用。" color: "顏色" opacity: "不透明度" @@ -2414,13 +2378,9 @@ zh_TW: none_selected: "選擇一個電子郵件範本開始編輯" revert: "恢復變更" revert_confirm: "你確定要恢復這個變更?" - css_html: - title: "CSS/HTML" - long_title: "CSS 與 HTML 客製化" colors: title: "顏色" long_title: "顏色樣式" - about: "不需撰寫 CSS 即可修改網站的顏色。請新增一項方案來開始使用。" new_name: "新的配色樣式" copy_name_prefix: "複製於" delete_confirm: "刪除此顏色樣式?" @@ -2559,8 +2519,6 @@ zh_TW: change_trust_level: "修改信任等級" change_username: "修改用戶名稱" change_site_setting: "修改網站設定" - change_site_customization: "修改網站客製化" - delete_site_customization: "刪除網站客製化" change_site_text: "更改站點文字" suspend_user: "將用戶停權" unsuspend_user: "恢復用戶權限" @@ -2691,8 +2649,6 @@ zh_TW: blocked: "已封鎖?" staged: "暫存?" show_admin_profile: "管理員" - edit_title: "編輯頭銜" - save_title: "儲存頭銜" refresh_browsers: "強制瀏覽器重新整理" refresh_browsers_message: "訊息已寄出給所有用戶!" show_public_profile: "顯示公開的基本資料" diff --git a/config/locales/server.ar.yml b/config/locales/server.ar.yml index 5511422199..5fc265524a 100644 --- a/config/locales/server.ar.yml +++ b/config/locales/server.ar.yml @@ -27,7 +27,6 @@ ar: topics: "المواضيع" posts: "المشاركات" loading: "يحمّل" - powered_by_html: 'تدعمه دسكورس ، يفضّل عرضه وجافاسكربت مفعّل' log_in: "لِج" purge_reason: "حُذف آليًّا باعتباره حسابًا إمّا مهجورًا أو غير مفعّل" disable_remote_images_download_reason: "عُطّل تنزيل الصور عن بعد بسبب نفاذ مساحة القرص الحرّة." @@ -318,16 +317,12 @@ ar: attributes: hex: invalid: "ليس لونا صالحا" - post_reply: - base: - different_topic: "المشاركة والرد لابد ان يكونا في نفس الموضوع" web_hook: attributes: payload_url: invalid: "رابط غير صالح. يجب أن يحتوي الرابط على http:// او https://. المساحة الفارغة غير مسموح بها." <<: *errors user_profile: - no_info_me: "
    معلوماتك الشخصية لم يتم إضافتها بعد هل تريد إضافتها الآن؟
    " no_info_other: "
    %{name} لم يدخلوا أي شيء في مجال المعلومات عني لملفهم بعد
    " vip_category_name: "استراحة" vip_category_description: "فئة خاصة للمستخدمين ذوي مستوى الثقة 3 فأعلى." @@ -615,6 +610,9 @@ ar: welcome_to: "مرحبا بك في %{site_name}!" approval_required: "يجب على المشرف أن يقبل حسابك الجديد قبل أن تستطيع الدخول لهذا المنتدى. سوف تتلقى بريد إلكتروني عندما يتم قبول حسابك." missing_session: "لا نستظيع تحديد اذا كان حسابك قد أنشئ. رجاء التأكد من أن (الكوكيز) بالمتصفح الذى تستخدمة منشطة." + admin_confirm: + description: "أمتأكّد من جعل %{target_username} مديرًا؟" + complete: "المستخدم %{target_username} مدير الآن." post_action_types: off_topic: title: 'خارج الموضوع' @@ -693,9 +691,9 @@ ar: topic_description: "لإعادة الاشتراك لل %{link}، استخدم ادارة التنبيهات في أسفل او يمين الموضوع." unsubscribe: title: "إلغاء الإشتراك" - stop_watching_topic: "توقف عن مراقبة هذا الموضوع، %{link}" - mute_topic: "الغاء جميع التنبيهات لهذا الموضوع، %{link}" - unwatch_category: "توقف عن مراقبة جميع المواضيع فى %{category}" + stop_watching_topic: "ألغِ مراقبة هذا الموضوع، %{link}" + mute_topic: "اكتم كلّ إشعارات هذا الموضوع، %{link}" + unwatch_category: "ألغِ مراقبة كلّ المواضيع في %{category}" mailing_list_mode: "الغاء وضعية قائمة المراسلات" disable_digest_emails: "توقف عن ارسال الملخصات بالبريد الالكترونى" all: "لا ترسل إليّ أيّ بريد من %{sitename}" @@ -708,7 +706,7 @@ ar: read: "قراءة" read_write: "قراءة/كتابة" description: "\"%{application_name}\": يطلب الصلاحيات التالية لحسابك" - no_trust_level: "عذرا، ليس لديك درجة الموثوقية الكافية لدخول واجهة برمجة التطبيق الخاصة بالمستخدم." + no_trust_level: "نأسف، لست بمستوى الثّقة الكافي للوصول إلى واجهة برمجة التّطبيقات (API) للمستخدمين" generic_error: "نأسف، لا نستطيع إصدار مفاتيح API للمستخدمين؛ عطّل مدير الموقع هذه الميزة" scopes: message_bus: "تحديثات فورية" @@ -718,16 +716,16 @@ ar: write: "كتابة الكلّ" reports: visits: - title: "زيارات العضو" - xaxis: "يوم" - yaxis: "عدد الزيارات" + title: "زيارات المستخدم" + xaxis: "اليوم" + yaxis: "مرّات الزّيارة" signups: - title: "أعضاء جدد" - xaxis: "يوم" - yaxis: "عدد الأعضاء الجدد" + title: "المستخدمون الجدد" + xaxis: "اليوم" + yaxis: "عدد المستخدمين الجدد" profile_views: title: "مشاهدات الحساب الشخصي للعضو" - xaxis: "يوم" + xaxis: "اليوم" yaxis: "عدد مشاهدات الحساب الشخصي العضو" topics: title: "المواضيع" @@ -739,16 +737,16 @@ ar: yaxis: "عدد المشاركات الجديدة" likes: title: "اﻹعجابات" - xaxis: "يوم" + xaxis: "اليوم" yaxis: "عدد اﻹعجابات الجديدة" flags: title: "الأبلاغ" xaxis: "يوم" yaxis: "عدد الإبلاغات" bookmarks: - title: "المفضلة" - xaxis: "يوم" - yaxis: "عدد العلامات المرجعية الجديدة." + title: "العلامات" + xaxis: "اليوم" + yaxis: "عدد العلامات الجديدة" starred: title: "متألق" xaxis: "يوم" @@ -756,9 +754,9 @@ ar: users_by_trust_level: title: "الأعضاء في مستوى الثقة" xaxis: "مستوى الثقة" - yaxis: "عدد اﻷعضاء" + yaxis: "عدد المستخدمين" emails: - title: "رسائل البريد الإلكتروني أرسلت" + title: "رسائل البريد المرسلَة" xaxis: "يوم" yaxis: "عدد رسائل البريد الإلكتروني" user_to_user_private_messages: @@ -889,7 +887,7 @@ ar: search_tokenize_chinese_japanese_korean: "ارغام الباحث على تجميع احرف اللغات الصينية واليابانية والكورية حتى على المواقع الغير معدة لتلك اللغات." allow_uncategorized_topics: "اسمح بتصميم النقاشات من غير فئات. تحذير: اذا كان هناك نقاشات غير مصنفه، يجب عليك تصنيفها قبل اغلاق هذا " allow_duplicate_topic_titles: "اسمح بالمواضيع المماثلة والعناوين المكررة." - unique_posts_mins: "كمية الدقائق التي يمكن للعضو قبلها إنشاء مشاركة مع نفس المحتوى مجددا" + unique_posts_mins: "عدد الدّقائق التي يمكن للمستخدم بعدها نشر مشاركة فيها نفس المحتوى ثانيةً" educate_until_posts: "عندما يبدا المستخدم كتابه منشورهم الاول.، اظهر انبثاق لوحه تعليمات المستخدم الجديد في المؤلف" title: "اسم هذا الموقع، يُستخدم في وسم title." site_description: "صِف هذا الموقع بجملة واحدة، يُستخدم في وسم meta description." @@ -902,12 +900,12 @@ ar: download_remote_images_max_days_old: "لا تقم بتحميل الصور من مواقع اخرى للمشاركات اﻷقدم من عدد س من اﻷيام." disabled_image_download_domains: "الصور البعيدة لن يتم تنزيلها من هذا المجال. قائمة Pipe-delimited." editing_grace_period: "خلال (n) ثواني بعد نشر المشاركة ، التعديل لن يقوم بانشاء نسخة جديدة في السجل الخاص بالتعديل ." - post_edit_time_limit: "الكاتب يتستطيع تعديل و مسح مشاركته لعده (n) دقائق بعد النشر. اضبطه للعدد0 للابد" + post_edit_time_limit: "يمكن للكاتب تحرير مشاركته أو حذفها لمدّة (n) دقيقة بعد نشرها. اضبطه إلى 0 ليكون للأبد." edit_history_visible_to_public: "اسمح لاي شخص ان يرى النسخ السابقه للمنشورات المعدله. عندما يتم تعطيلها.،فقط اعضاء فريق العمل يمكنهم رؤيتها " delete_removed_posts_after: "المشاركات التي تم إزالتها عن طريق الكتاب سيتم مسحها اوتوماتيكياً بعد(n) ساعات ة. اذا تم ضبطها علي 0، سيتم مسح المشاركه فوراً" max_image_width: "أقصى عرض للصور المصغرة في مشاركة" max_image_height: "أقصى ارتفاع للصور المصغرة في مشاركة" - fixed_category_positions: "إذا تم التحقق, ستتمكن من ترتيب الفئات على شكل المطلوب. وإذا لم يتم التحقق, ستسرد الفئات على حسب الفعالية." + fixed_category_positions: "إن أُشّر، فسيمكنك ترتيب الفئات بترتيب ثابت. إن لم يؤشّر، فستُسرد الفئات بترتيب نشاطها." fixed_category_positions_on_create: "إذا تحققت، سيحفظ ترتيب الفئة في موضوع الحوار المنشأ (يتطلب fixed_category_positions)." add_rel_nofollow_to_user_content: "أضف rel nofollow لجميع محتوى المستخدم المتقدم، باستثناء الروابط الداخلية (بما في ذلك المجالات الأصل). إذا غيرت هذا، يجب عليك عمل rebake لكل المشاركات بـ: \"rake posts:rebake\"" exclude_rel_nofollow_domains: "قائمه النطاقات حيت لا اتباع لا يجب ان تضاف للروابط. tld.com سوف يقوم تلقائيا بالسماح sub.tld.com كذلك مثل الاصغر , يجب عليك اضافه النطاق الاعلى مستوي الخاص بالموقع لمساعده زحف الويب ليجاد جميع المحتويات.اذا كان باقي اجزاء موقعك في نطاقات اخرى , اضفهم ايضا" @@ -977,13 +975,13 @@ ar: topics_per_period_in_top_page: "عدد من أهم الموضوعات هو مبين في موجز أعلى الافتراضية المواضيع." redirect_users_to_top_page: "إعادة توجيه تلقائيا للمستخدمين الجدد وغائبة لمدة طويلة إلى أعلى الصفحة." top_page_default_timeframe: "الوقت المثبت علي الصفحة الأكثر مشاهدة." - show_email_on_profile: "إظهار بريدك المستخدم على صفحته الشخصية (مرئية فقط لأنفسهم والموظفين)" - email_token_valid_hours: "نسيت كلمه السر/ تنشيط رموز الحساب صالحه ل(n) ساعات" + show_email_on_profile: "أظهر بريد المستخدمين الإلكترونيّ في لاحاتهم (يكون معروضًا لهم وللطّاقم)" + email_token_valid_hours: "أمارات نسيان كلمة السّرّ/تنشيط الحساب صالحة لمدّة (n) ساعة." enable_badges: "تفعيل نظام الأوسمة." allow_index_in_robots_txt: "حدّد في robots.txt إمكانيّة فهرسة محرّكات البحث في الوبّ هذا الموقع." email_domains_blacklist: "قائمة pipe-delimited المجالات البريد الإلكتروني الذي لا يسمح للمستخدمين تسجيل حسابات مع. مثال: mailinator.com | trashmail.net" email_domains_whitelist: "قائمة pipe-delimited من مجالات البريد الإلكتروني التي يجب على المستخدمين تسجيل حسابات مع. تحذير: لن يسمح للمستخدمين مع مجالات البريد الإلكتروني الأخرى غير المذكورة هنا!" - forgot_password_strict: "عدم اعلام المستخدم بوجود حساب عند استخدامهم استمارة \"نسيت كلمة السر\"" + forgot_password_strict: "لا تخبر المستخدمين بوجود الحساب عند استخدامهم حوار نسيان كلمة السّرّ." log_out_strict: "عند الخروج، اخرج من كلّ جلسات المستخدم في كلّ الأجهزة" version_checks: "Ping ديسكورس مركزا لتحديثات الإصدار وإظهار الرسائل النسخة الجديدة على لوحة القيادة / مسؤول" new_version_emails: "إرسال بريد إلكتروني إلى عنوان contact_email عندما نسخة جديدة من ديسكورس هو متاح." @@ -1070,6 +1068,8 @@ ar: external_system_avatars_enabled: "استخدم خدمات الهه النظام الخارجي " default_opengraph_image_url: "عنوان رابط صورة ال اوبن جراف الثابتة" allow_all_attachments_for_group_messages: "اسمح بجميع ملحقات البريد للرسائل الجماعيه " + convert_pasted_images_to_hq_jpg: "حوّل الصّور الملصقة إلى ملفّات JPG عالية الجودة." + convert_pasted_images_quality: "جودة ملفّات JPG المحوّلة (1 هي أدنى جودة، 100 هي أفضل جودة)." enable_flash_video_onebox: "فعّل تضمين روابط swf وflv (أدوبي فلاش) في الصّناديق. تحذير: قد يتسبّب بمخاطر أمنيّة." default_invitee_trust_level: "مستوى الثقة الإفتراضي (0-4) للأعضاء المدعوين." default_trust_level: "مستوى الثقة الإفتراضي (0-4) للأعضاء المدعوين.تحذير! التغيرات ستضعك تحت خطر البريد الغير هام." @@ -1176,7 +1176,7 @@ ar: delete_all_posts_max: "العدد الاقص للمشاركات التي يمكن مسحها مره واحده بزر مسح كل المشاركات.اذا المستخدم لديه اكثر من هذا العدد من المشاركات, المشاركات لا يمكن مسح جميعها مره واحدهو المستخدم لا يمكن حذفه" username_change_period: "عدد الايام بعد التسجيل التي يستطيع فيها الحسابات بتغير اسم المستخدم الخاص بهم(0 لكي لا تسمح بتغير اسم المستخدم)" email_editable: "يُسمح للأعضاء بتغيير بريدهم الألكتروني بعد التسجيل." - allow_uploaded_avatars: "اسمح للمستخدمين بتحميل صور شخصيه مخصصه" + allow_uploaded_avatars: "اسمح للمستخدمين برفع صور لاحة مخصّصة." allow_animated_avatars: "يُسمح للمستخدمين باستخدام صور متحركة GIF كصورة شخصية.\nتحذير:يُمنع استخدام صور رمزية خليعة:سيتم تحديث الصورة فور تغييرها." allow_animated_thumbnails: "يولد الصور المصغرة المتحركة للرسوم المتحركة. " default_avatars: "سيتم استخدام عناوين URL للصور الرمزية بشكل افتراضيًا للأعضاء الجدد حتى يغيروا صورهم." @@ -1191,7 +1191,7 @@ ar: anonymous_posting_min_trust_level: "أدنى مستوى ثقة للسماح بنشر مشاركات مجهولة." anonymous_account_duration_minutes: "للحمايه الهويه اصنع حساب مجهول كل n دقائق لكل مستخدم.\nمثال:اذا ضبط 600 , بعد انقضي 600 دقيقه من اخر منشور و غير المستخدم للمجهول.تم انشاء حساب مجهول." hide_user_profiles_from_public: "قم بإلغاء الصفحة الشخصية و حقيبة المستخدم و بطاقة المستخدم للمستخدمين المجهولين" - allow_profile_backgrounds: "اسمح للأعضاء برفع خلفيات ملف التعريف." + allow_profile_backgrounds: "اسمح للمستخدمين برفع خلفيّات للّاحة." sequential_replies_threshold: "عدد المشاركات التي يجب على المستخدم انشاءها في عمود في الموضوع الواحد قبل ان يتم تنبيه بكثره الردود المتتاليه ." enable_mobile_theme: "اجهزه الموبايل تستخدم ثيم مناسب للجوال,امكانيه التحويل للموقع الكامل .الغي هذا اذا اردت استخدام انماط سريعه الاستجابه" dominating_topic_minimum_percent: "ما هي النسبه المئويه للمشاركات التي يجب علي المستخدم عملها في الموضوع قبل تلقيه لتنبيه عن خروجه لنطاق الموضوع" @@ -1363,7 +1363,6 @@ ar: incorrect_username_email_or_password: "اسم المستخدم او كلمة المرور او البريد الالكتروني غير صحيح" wait_approval: "شكرًا على التسجيل. سيتم إبلاغك عندما تتم عملية الموافقة على حسابك." active: "حسابك مفعل وجاهز للاستخدام." - activate_email: "انتهيت تقريبا! تم ارسال رسالة تفعيل لبريدك : %{email} رجاءا اتبع التعليمات المرفقة في الرسالة لتفعيل حسابك.

    اذا لم تصلك رسالة التفعيل، تفقد مجلد البريد الغير هام، أو حاول تسجيل الدخول مرة أخرى لإرسال رسالة تفعيل أخرى.

    " not_activated: "لا يمكنك تسجيل الدخول حتى الان. أرسلنا رسالة تنشيط لك. يرجى اتباع التعليمات الواردة في البريد الإلكتروني لتفعيل حسابك" not_allowed_from_ip_address: "ﻻ يمكنك تسجيل الدخول كـ %{username} من هذا الـIP" admin_not_allowed_from_ip_address: "لا يمكنك تسجيل الدخول كمدير من خلال هذا العنوان الرقمي - IP." @@ -1408,22 +1407,22 @@ ar: many: "%{count} تبليغات تنتظر التعامل معها" other: "%{count} تبليغات تنتظر التعامل معها" unsubscribe_mailer: - subject_template: "اكد انك لا تريد استقبال بريد الكتروني من %{site_title} بعد ألان" + subject_template: "أكّد عدم رغبتك بعد الآن باستقبال تحديثات %{site_title} على البريد" invite_mailer: - subject_template: "%{invitee_name} دعاك إلى '%{topic_title}' على %{site_domain_name}" + subject_template: "دعاك %{invitee_name} إلى ’%{topic_title}‘ على {site_domain_name}" + custom_invite_mailer: + subject_template: "دعاك %{invitee_name} إلى ’%{topic_title}‘ على {site_domain_name}" invite_forum_mailer: - subject_template: "%{invitee_name} قام بدعوتك للإنضمام إلى %{site_domain_name}" + subject_template: "دعاك %{invitee_name} للانضمام إلى {site_domain_name}" custom_invite_forum_mailer: subject_template: "دعاك %{invitee_name} للانضمام إلى %{site_domain_name}" invite_password_instructions: - subject_template: "تعيين كلمة مرور %{site_name} حسابك" + subject_template: "اضبط كلمة سرّ لحسابك على %{site_name}" + admin_confirmation_mailer: text_body_template: | - شكرا لقبول دعوتك إلى %{site_name} -- أهلا بك! + رجاء أكّد رغبتك بإضافة **%{target_username}** كمدير لمنتداك. - انقر فوق هذا الرابط لاختيار كلمة مرور الآن: - %{base_url}/users/password-reset/%{email_token} - - (إذا انتهت مدة صلاحية الرابط أعلاه، اختر "نسيت كلمة المرور" عند تسجيل الدخول مع عنوان البريد الإلكتروني الخاص بك.) + [أكّد حساب المدير](%{admin_confirm_url}) new_version_mailer: subject_template: "[%{email_prefix}] إصدارة دسكورس جديدة، ثمّة تحديث متوفّر" text_body_template: | @@ -1448,7 +1447,6 @@ ar: disagreed: "نشكرك لإعلامنا بذلك. نحن ننظر في الأمر." deferred: "نشكرك لإعلامنا بذلك. نحن ننظر في الأمر." deferred_and_deleted: "نشكرك لإعلامنا بذلك. لقد أزلنا المشاركة." - temporarily_closed_due_to_flags: "هذا الموضوع مغلق مؤقتا بسبب عدد التبليغات الكبير للمجتمع." system_messages: post_hidden: title: "المشاركة مخفية" @@ -1535,61 +1533,8 @@ ar: digest: why: "ملخص المؤجز من %{site_link} منذ زيارتك الاخيره في %{last_seen_at}" click_here: "أنقر هنا" - forgot_password: - text_body_template: | - طلب أحدهم تصفير كلمة السّر لحسابك في [%{site_name}](%{base_url}). - - إن لم يكن أنت ذلك الشّخص، فيمكنك تجاهل هذا البريد بأمان. - - انقر الرّابط أدناه لاختيار كلمة سرّ جديدة: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - شخص ما طلب إضافة كلمة مرور لحسابك [%{site_name}](%{base_url}). بدلاً من ذلك، يمكنك تسجيل الدخول بإستخدام خدمات أخرى على الانترنت، مثل (قوقل، فيس بوك,الخ...) سيتم اقران البريد الالكتروني والتحقق من صحته. - - اذا لم تقم أنت بطلب هذا، يمكنك تجاهل هذه الرسالة الالكترونية بأمان. - - أنقر على الرابط التالي لإختيار كلمة المرور : - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - شخص طلب تسجيل الدخول إلى الحساب الخاص بك في [%{site_name}](%{base_url}). - - إذا كنت لم تتقدم بهذا الطلب، يمكنك تجاهل هذه الرسالة الإلكترونية بأمان. - - اضغط على الرابط التالي للدخول: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - تم إنشاء حساب جديد بالنسبة لك في %{site_name} - - انقر على الرابط التالي لاختيار كلمة مرور لحسابك الجديد: - %{base_url}/users/password-reset/%{email_token} signup_after_approval: subject_template: "قد وافقت على %{site_name}!" - text_body_template: | - مرحبا بك الي %{site_name}! - - اعضاء الطاقم قاموا بالموافقه علي حسابك علي %{site_name} - - اضغط الرابط التالي للتاكيد و لتفعيل حسابك الجديد - base_url}/users/activate-account/%{email_token}% - اذا كان الرابط في الاعلى غير مضغوط , حاول نسخه و لصقه في ششريط العناوين الالكتروني في متصفح الويب خاصتك. - {new_user_tips}% - نح نؤمن في [سلوك المجتمع الراقي(base_url}/guidelines}%) في كل الاوقات - - تمتع ببقائك! - - (اذا احتجت للاتصال مع [افراد الطاقم]({base_url}/about}%)كمستخدم جديد , فقط قم بالرد علي هذه الرساله). - signup: - text_body_template: | - أهلا وسهلا بك في %{site_name}! - - أُنقر على الرابط التالي لتأكيد وتفعيل حسابك الجديد : - - %{base_url}/users/activate-account/%{email_token} - - إذا كان الرابط أعلاه غير قابل للنقر، حاول نسخه ولصقه في شريط العناوين في متصفح الويب عندك. page_not_found: popular_topics: "نَشط" recent_topics: "الأحدث" @@ -1753,9 +1698,11 @@ ar: name: قائد welcome: name: مرحبًا - description: استقبلت إعجابًا + description: استقبلَ إعجابًا long_description: | تُمنح هذه الشّارة عندما تستقبل أولى الإعجابات لمشاركاتك. تهانينا. لقد نشرت شيئًا وجده أصدقاؤك أعضاء المجتمع أمرًا جديرًا بالاهتمام ومفيد! + autobiographer: + description: ملأ معلوماتاللاحة anniversary: name: الذكرى السنوية nice_post: @@ -1774,8 +1721,6 @@ ar: name: أول إعجاب first_flag: name: أول إبلاغ - promoter: - description: عضو غير صحيح first_share: name: أول مشاركة first_link: @@ -1786,19 +1731,20 @@ ar: name: إقرأ القواعد العامة reader: name: قارئ + description: قرأ كلّ الرّدود في موضوع فيه أكثر من 100 ردّ popular_link: name: رابط شائع hot_link: name: رابط نَشط famous_link: name: رابط مشهور - appreciated: - name: مشكور respected: name: محترم + higher_love: + description: استخدمَ 50 إعجابًا يوميًّا 5 مرّات crazy_in_love: name: مجنون الحبّ - description: استخدمت 50 إعجابًا يوميًّا 20 مرّة + description: استخدمَ 50 إعجابًا يوميًّا 20 مرّة long_description: | تُمنح هذه الشّارة عندما تستخدم كلّ ال‍ 50 إعجابًا اليوميّة لمدّة 20 يومًا. مذهل! أصبحت أيقونة تشجّع بانتظام أصدقائها أعضاء المجتمع! thank_you: @@ -1812,7 +1758,9 @@ ar: description: استخدمَ إيموجي في مشاركة first_mention: name: أوّل إشارة - description: أشرت إلى مستخدم في مشاركة + description: أشار إلى مستخدم في إحدى المشاركات + first_reply_by_email: + name: أوّل ردّ عبر البريد admin_login: success: "البريد أُرسل" error: "خطأ!" diff --git a/config/locales/server.bs_BA.yml b/config/locales/server.bs_BA.yml index b7aff2a3f4..f9e96e443d 100644 --- a/config/locales/server.bs_BA.yml +++ b/config/locales/server.bs_BA.yml @@ -27,7 +27,6 @@ bs_BA: topics: "Topics" posts: "posts" loading: "Loading" - powered_by_html: 'Powered by Discourse, best viewed with JavaScript enabled' log_in: "Log In" purge_reason: "Automatski obrisan kao napušten, deaktiviran račun" disable_remote_images_download_reason: "Remote images download was disabled because there wasn't enough disk space available." @@ -131,7 +130,6 @@ bs_BA: invalid: "is not a valid color" <<: *errors user_profile: - no_info_me: "
    the About Me field of your profile is currently blank, would you like to fill it out?
    " no_info_other: "
    %{name} hasn't entered anything in the About Me field of their profile yet
    " vip_category_name: "Lounge" vip_category_description: "A category exclusive to members with trust level 3 and higher." @@ -635,7 +633,6 @@ bs_BA: incorrect_username_email_or_password: "Netačan nadimak, email ili šifra" wait_approval: "Hvala vam što ste se prijavili. We will notify you when your account has been approved." active: "Vaš nalog je aktivan i spreman za korišćenje." - activate_email: "

    Skoro ste gotovi! Poslali smo email za aktivaciju na %{email}. Molimo vas da pratite upustva u emailu da bi ste aktivirali vaš nalog.

    Ako email ne stigne, provjerite SPAM folder, ili se probajte ulogovat opet da bi poslali email za aktivaciju.

    " not_activated: "You can't log in yet. We sent an activation email to you. Please follow the instructions in the email to activate your account." not_allowed_from_ip_address: "You can't login as %{username} from that IP address." suspended: "You can't log in until %{date}." @@ -738,53 +735,8 @@ bs_BA: digest: why: "Ukratko šta se dešavalo na %{site_link} od vaše zadnje posjete %{last_seen_at}." click_here: "klikni ovdje" - forgot_password: - text_body_template: | - Potražena je obnova šifre na [%{site_name}](%{base_url}). - - Ako je niste vi zatražili ignirišite ovaj email. - - Kliknite ovdje da resetujete vašu šifru: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Somebody asked to add a password to your account on [%{site_name}](%{base_url}). Alternatively, you can log in using any supported online service (Google, Facebook, etc) that is associated with this validated email address. - - If you did not make this request, you can safely ignore this email. - - Click the following link to choose a password: - %{base_url}/users/password-reset/%{email_token} - account_created: - text_body_template: | - Novi nalog je kreiran za vas na %{site_name} - - Kliknite na link ispod da kreirate šifru za vaš nalog: - %{base_url}/users/password-reset/%{email_token} signup_after_approval: subject_template: "You've been approved on %{site_name}!" - text_body_template: | - Welcome to %{site_name}! - - A staff member approved your account on %{site_name}. - - Click the following link to confirm and activate your new account: - %{base_url}/users/activate-account/%{email_token} - - If the above link is not clickable, try copying and pasting it into the address bar of your web browser. - - %{new_user_tips} - - We believe in [civilized community behavior](%{base_url}/guidelines) at all times. - - Enjoy your stay! - signup: - text_body_template: | - Dobrodošli na %{site_name}! - - Kliknite na link ispod za aktovaciju vašeg naloga: - %{base_url}/users/activate-account/%{email_token} - - Ako link iznad ne može da se klikne onda ga kopirajte i ubacite u vaš internet pretraživač. page_not_found: popular_topics: "Popularne" recent_topics: "Nove" diff --git a/config/locales/server.cs.yml b/config/locales/server.cs.yml index a0c0edda79..4c372e8e78 100644 --- a/config/locales/server.cs.yml +++ b/config/locales/server.cs.yml @@ -27,7 +27,6 @@ cs: topics: "Témata" posts: "příspěvky" loading: "Nahrávám" - powered_by_html: 'Systém běží na Discourse, nejlépe funguje se zapnutým JavaScriptem' log_in: "Přihlásit se" purge_reason: "Tento účet byl automaticky smazán a deaktivován jako opuštěný" disable_remote_images_download_reason: "Stahování obrázků z cizích serverů bylo vypnuto protože na disku není dostatek místa." @@ -204,12 +203,8 @@ cs: attributes: hex: invalid: "není platná barva" - post_reply: - base: - different_topic: "Příspěvek a odpověď musí náležet stejnému topicu" <<: *errors user_profile: - no_info_me: "
    Pole 'o mně' je v tuto chvíli prázdné, nechcete si ho vyplnit?
    " no_info_other: "
    %{name} o sobě zatím žádné informace nevyplnil
    " vip_category_name: "VIP" vip_category_description: "Kategorie je přístupná výhradně členům s důvěryhodností 3 a vyšší." @@ -236,6 +231,7 @@ cs: few: "Tato kategorie nejde smazat protože obsahuje %{count} témata. Nejstarší téma je %{topic_link}." other: "Tato kategorie nejde smazat protože obsahuje %{count} témat. Nejstarší téma je %{topic_link}." topic_exists_no_oldest: "Kategorii nelze smazat protože počet příspěvků je %{count}." + uncategorized_description: "Témata bez kategorií, případně která nepasují do stávajících kategorií." trust_levels: newuser: title: "nový uživatel" @@ -623,7 +619,7 @@ cs: title_fancy_entities: "Převádět běžné ASCII znaky na HTML entity v názvech témat, ve stylu SmartyPants http://daringfireball.net/projects/smartypants/" title_prettify: "Zabrání běžným překlepům a chybám v názvu tématu, včetně psaní velkými písmeny, malé písmeno na začátku, vícenásobné vykřičníky a otazníky, atd." faq_url: "Pokud máte dokument FAQ hostovaný samostatně, napište sem jeho plnou URL." - tos_url: "Pokud máte dokument 'Podmínky Používání' hostovaný samostatně, napište sem jeho plnou URL." + tos_url: "Pokud máte dokument 'Podmínky používání' hostovaný samostatně, napište sem jeho plnou URL." privacy_policy_url: "Pokud máte dokument 'Ochrana Soukromí' hostovaný samostatně, napište sem jeho plnou URL." reply_by_email_address: "Šablona emailé adresy pro odpověď emailem, např. %{reply_key}@reply.myforum.com" delete_all_posts_max: "Maximální počet příspěvků, které mohou být smazány najednou tlačítkem 'Odstranit všechny příspěvky'. Pokud má uživatel více příspěvků než je zde nastaveno, nemohou být jeho příspěvky smazány najednou a uživatele nelze odstranit." @@ -708,32 +704,10 @@ cs: posted_by: "Zaslal uživatel %{username} dne %{post_date}" digest: click_here: "klikněte zde" - forgot_password: - text_body_template: | - Někdo zažádal o obnovení vašeho hesla na [%{site_name}](%{base_url}). - - Pokud jste to nebyli vy, můžete tento email ignorovat. - - Klikněte na následující odkaz pro vytvoření nového hesla: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Somebody asked to add a password to your account on [%{site_name}](%{base_url}). Alternatively, you can log in using any supported online service (Google, Facebook, etc) that is associated with this validated email address. - - If you did not make this request, you can safely ignore this email. - - Click the following link to choose a password: - %{base_url}/users/password-reset/%{email_token} + account_created: + title: "Účet vytvořen" signup_after_approval: subject_template: "Byli jste schváleni na %{site_name}!" - signup: - text_body_template: | - Vítejte na %{site_name}! - - Kliknutím na následující odkaz potvrdíte a aktivujete svůj nový účet: - %{base_url}/users/activate-account/%{email_token} - - Pokud tento odkaz nefunguje, zkuste ho zkopírovat a vložit přes schránku do adresního řádku vaše webového prohlížeče. page_not_found: popular_topics: "Populární" recent_topics: "Nedávné" @@ -744,7 +718,7 @@ cs: #[Vítejte na %{title}](#welcome) Je vyžadován uživatelský účet. Prosím vytvořte si nový účet nebo se přihlaste. terms_of_service: - title: "Podmínky používání." + title: "Podmínky používání" signup_form_message: 'I have read and accept the Terms of Service.' deleted: 'smazáno' upload: diff --git a/config/locales/server.da.yml b/config/locales/server.da.yml index 8cc67ab96b..da593c43b9 100644 --- a/config/locales/server.da.yml +++ b/config/locales/server.da.yml @@ -15,6 +15,7 @@ da: short: "%d-%m--%Y" short_no_year: "%-d. %B" date_only: "%-d. %B, %Y" + long: "%-d. %B %Y, %H:%M" date: month_names: [null, Januar, Februar, Marts, April, Maj, Juni, Juli, August, September, Oktober, November, December] <<: *datetime_formats @@ -26,13 +27,14 @@ da: topics: "Emner" posts: "indlæg" loading: "Indlæser" - powered_by_html: 'Siden er lavet med Discourse, opleves bedst med JavaScript slået til' log_in: "Log ind" purge_reason: "Automatisk slettet som forladt, deaktiveret konto" disable_remote_images_download_reason: "Fjerndownload af billeder deaktiveret grundet utilstrækkelig diskplads" anonymous: "Anonym" + remove_posts_deleted_by_author: "Slettet af forfatteren" emails: incoming: + default_subject: "Emnet mangler en titel" show_trimmed_content: "Vis trimmet indhold" maximum_staged_user_per_email_reached: "Maksimalt antal af brugere oprettet pr. email." errors: @@ -68,6 +70,8 @@ da: has_already_been_used: "er allerede blevet brugt" inclusion: er ikke inkluderet i listen invalid: er ikke gyldig + is_invalid: "virker upræcis, er det en hel sætning?" + contains_censored_words: "indeholder følgende censurerede ord: %{censurerede ord}" less_than: skal være mindre end %{count} less_than_or_equal_to: skal være mindre end eller lig med %{count} not_a_number: er ikke et nummer @@ -101,12 +105,18 @@ da: max_username_length_range: "Du kan ikke sætte maksimum til mindre end minimum." default_categories_already_selected: "Du kan ikke vælge en kategori der er brugt i en anden liste." s3_upload_bucket_is_required: "Du kan ikke oploade til S3 med mindre du har angivet 's3_upload_bucket'." + invite: + user_exists: "Der er ingen grund til at invitere %{email}, personen har allerede en profil!" + bulk_invite: + file_should_be_csv: "Den uploadede fil skal være i csv format." + error: "Der skete en fejl ved upload af filen. Prøv venligst igen senere." topic_invite: user_exists: "Beklager, men brugeren er allerede inviteret. Du kan kun invitere en bruger til et emne én gang." backup: operation_already_running: "Der kører allerede en %{operation}. Kan ikke starte et nyt %{operation} job lige nu." backup_file_should_be_tar_gz: "Backup filen skal være et .tar.gz arkiv." not_enough_space_on_disk: "Der er ikke nok diskplads til at uploade denne backup." + invalid_filename: "Backup filnavnet indeholder ugyldige tegn. Gyldige tegn er a-z 0-9 . - _." not_logged_in: "Du skal logge på for at kunne dette." not_found: "Den anmodede URL eller ressource kunne ikke findes." invalid_access: "Du har ikke tilladelse til at se den anmodede ressource." @@ -119,6 +129,7 @@ da: embed: start_discussion: "Start diskussion" continue: "Fortsæt diskussion" + error: "Fejl ved indlejring" more_replies: one: "1 svar til" other: "%{count} svar til" @@ -243,12 +254,16 @@ da: too_many_users: "Du kan kun sende advarsler til en bruger ad gangen." cant_send_pm: "Beklager, du kan ikke sende en privat besked til den bruger." no_user_selected: "Du skal vælge en gyldig bruger." + featured_link: + invalid: "er ugyldig. URL'en skal indeholde http:// eller https://. " user: attributes: password: common: "er én af de 10000 mest almindelige adgangskoder. Brug venligst en mere sikker adgangskode." same_as_username: "er det samme som dit brugernavn. Brug venligst en mere sikker adgangskode." same_as_email: "er det samme som din email. Brug venligst en mere sikker adgangskode." + same_as_current: "er det samme som din nuværende adgangskode." + unique_characters: "har for mange af de samme tegn. Brug venligst en mere sikkert adgangskode." ip_address: signup_not_allowed: "Tilmelding er ikke tilladt fra den aktuelle adresse." color_scheme_color: @@ -257,10 +272,21 @@ da: invalid: "er ikke en gyldig fave" post_reply: base: - different_topic: "Indlæg og svar skal tilhøre samme emne." + different_topic: "Indlæg og svar skal tilhøre det samme emne." + web_hook: + attributes: + payload_url: + invalid: "URL'en er ugyldig. URL'en skal indeholde http:// eller https://. Mellemrum er ikke tilladt." + custom_emoji: + attributes: + name: + taken: er allerede i brug af en anden emoji + topic_status_update: + attributes: + execute_at: + in_the_past: "skal være i fremtiden." <<: *errors user_profile: - no_info_me: "
    “Om mig” feltet på din profil er pt. tomt, ønsker du at udfylde det?
    " no_info_other: "
    %{name} har ikke skrevet noget i “Om mig”-feltet endnu
    " vip_category_name: "Lounge" vip_category_description: "En ekslusiv kategori der kun er tilgængelig for medlemmer med tillidsniveu 3 eller højere." @@ -269,6 +295,8 @@ da: staff_category_name: "Staff" staff_category_description: "Privat gruppe for staff diskussioner. Emner er kun synlige for administratorer og moderatorer." assets_topic_body: "Dette er et permanent emne, kun synlig for Staff, til at gemme billeder og filer som bruges til Site designet. Slet ikke dette emne!\n\n\nSådan virker det:\n\n\n1. Svar dette emne.\n2. Upload alle billeder som du ønsker at bruge til logoer, favicons etc her. (Brug upload ikonet i værktøjslinjen eller drag n drop billederne.)\n3. Send dit svar for at indsende det.\n4. Højreklik på billederne i dit svar for at se stien til de uploadede billeder, eller klik på ikonet for at redigere dit indlæg og find stierne til billederne der. Kopier billedestierne.\n5. Indsæt disse billedestier i [basic settings](/admin/site_settings/category/required).\n\n\nHvis du har brug for at tillade flere filtyper, så kan du redigere `authorized_extensions` i [file settings](/admin/site_settings/category/files)." + discourse_welcome_topic: + title: "Velkommen til Discourse" lounge_welcome: title: "Velkommen i loungen" body: |2 @@ -443,6 +471,12 @@ da: welcome_to: "Velkommen til %{site_name}!" approval_required: "En moderator skal godkende din nye konto før du kan tilgå forummet. Du får en e-mail, når din konto er godkendt!" missing_session: "Vi kan ikke se om din konto blev oprettet, venligst tjek at cookies er accepteret." + activated: "Desværre, denne konto er allerede aktiveret." + admin_confirm: + title: "Bekræft Admin Konto" + description: "Er du sikker på du vil gøre %{target_username} til administrator?" + grant: "Tildel Admin Adgang" + complete: "%{target_username} er nu administrator." post_action_types: off_topic: title: 'Uden for emnet' @@ -482,6 +516,11 @@ da: title: 'Stem' description: 'Stem for dette indlæg' long_form: 'stemte for dette indlæg' + user_activity: + no_bookmarks: + others: "Ingen bogmærker" + no_likes_given: + self: "Du har ikke liket nogen indlæg." topic_flag_types: spam: title: 'Spam' @@ -528,8 +567,12 @@ da: authorize: "Autoriser" read: "læst" read_write: "læs/skriv" + description: "\"%{application_name}\" anmoder om følgende adgang til din konto:" no_trust_level: "Beklager, du har ikke adgang til brugerens API" generic_error: "Beklager, vi er ikke i stand til at give dig API nøgler, denne funktionalitet er muligvis bestemt af sitets administrator" + scopes: + message_bus: "Live opdateringer" + notifications: "Læs og ryd notifikationer" reports: visits: title: "Brugerbesøg" @@ -793,6 +836,7 @@ da: show_email_on_profile: "Vis en brugers email på deres profil (kun synligt for brugerne selv - samt admin)" email_token_valid_hours: "Glemte password / konto aktiverings muligheder er gyldige i (n) timer." enable_badges: "Aktiver badge systemet" + enable_whispers: "Tillad at hjælperteamet har private samtaler i emner." log_out_strict: "Når brugeren logger af, log da ud af alle sessioner, på alle enheder" new_version_emails: "Send en email til contact_email adressen når der er en ny version af Discourse tilgængelig." port: "KUN FOR UDVIKLERE! ADVARSEL! Brug denne HTTP-port i stedet for standarden, som er port 80. Lad feltet stå tomt for at bruge standardværdien." @@ -821,10 +865,20 @@ da: active_user_rate_limit_secs: "Hvor ofte vi opdaterer feltet 'last_seen_at', i sekunder." previous_visit_timeout_hours: "Hvor lang tid et besøg varer før vi regner det med i det 'forrige' besøg, i timer." max_likes_per_day: "Maksimalt antal likes per bruger per dag." + max_flags_per_day: "Maksimalt antal flagmarkeringer per bruger per dag." clean_orphan_uploads_grace_period_hours: "Grace-periode (i timer) før et forældreløst upload bliver fjernet." purge_deleted_uploads_grace_period_days: "Grace-periode (i dage) før et slettet upload bliver fjernet." + twitter_summary_large_image_url: "URL til standard Twitter Summary Card billede (skal være mindst 280px i bredden og mindst 150px i højden)." + tl1_requires_topics_entered: "Hvor mange emner en ny bruger skal læse før vedkommende forfremmes til tillidsniveau 1." + tl1_requires_read_posts: "Hvor mange indlæg en ny bruger skal læse før vedkommende forfremmes til tillidsniveau 1." + tl1_requires_time_spent_mins: "Hvor mange minutter en ny bruger skal læse indlæg før vedkommende forfremmes til tillidsniveau 1." + tl2_requires_topics_entered: "Hvor mange emner en bruger skal læse før vedkommende forfremmes til tillidsniveau 2." + tl2_requires_read_posts: "Hvor mange indlæg en bruger skal læse før vedkommende forfremmes til tillidsniveau 2." + tl2_requires_time_spent_mins: "Hvor mange minutter en bruger skal læse indlæg før vedkommende forfremmes til tillidsniveau 2." + tl2_requires_days_visited: "Hvor mange dage en bruger skal besøge siden før vedkommende forfremmes til tillidsniveau 2." tl2_requires_likes_received: "Hvor mange likes en bruger skal modtage inden forfremmelse til tillidsniveau 2." tl2_requires_likes_given: "Hvor mange likes en bruger skal give inden forfremmelse til tillidsniveau 2." + tl3_requires_max_flagged: "Brugere må ikke have mere end x indlæg flagmarkeret af x forskellige brugere i løbet a de sidste (tl3 time period) dage for at kvalificere sig til forfremmelse til tillidsniveau 3. X er indstillingens værdi (0 eller mere)." min_trust_to_create_topic: "Det mindste tillidsniveau der skal til for at oprette et nyt emne." title_fancy_entities: "Omdan almindelige ASCII-tegn i emnetitler til fancy HTML-entities, i stil med SmartyPants http://daringfireball.net/projects/smartypants/" title_prettify: "Undgå hyppige tastefejl i titlen, inklusive overforbrug af store bogstaver, første bogstav med småt, gentagne ! og ?, ekstra . i slutningen, etc." @@ -833,6 +887,11 @@ da: faq_url: "Hvis du hoster en FAQ et andet sted kan du indtaste den fulde URL her." tos_url: "Hvis du hoster dine forretningsbetingelser et andet sted kan du indtaste den fulde URL her." privacy_policy_url: "Hvis du hoster din privatlivspolitik et andet sted kan du indtaste den fulde URL her." + staff_like_weight: "Hvor meget ekstra vægt har hjælperteamets likes." + max_new_accounts_per_registration_ip: "Hvis der allerede er (n) kontoer med tillidsniveau 0 fra en IP (og ingen er en del af hjælperteamet eller på TL2 eller højere), så accepteres nye kontooprettelser fra denne IP ikke." + num_flaggers_to_close_topic: "Minimum antal flagmarkeringer fra unikke brugere, der skal til for automatisk at pause et emne til ingriben" + num_flags_to_close_topic: "Minimum antal aktive flagmarkeringer, der skal til for automatisk at pause et emne til indgriben" + auto_respond_to_flag_actions: "Aktivér automatisk besvarelse, når en flagmarkering fjernes." auto_block_fast_typers_on_first_post: "Blokér automatisk brugere som ikke opfylder min_first_post_typing_time" auto_block_fast_typers_max_trust_level: "Maksimal trust level for at blokere \"fast typers\"" reply_by_email_enabled: "Aktivér muligheden for at svare til emner via email." @@ -848,6 +907,13 @@ da: delete_all_posts_max: "Det maksimale antal indlæg der kan slettes på én gang med “Slet alle indlæg”-knappen. Hvis en bruger har mere end dette antal indlæg, kan indlæggene ikke slettes på én gang og brugeren kan ikke slettes." username_change_period: "Antal dage efter oprettelsen hvor brugere kan ændre deres brugernavn (0 for ikke at tillade skift af brugernavn)." email_editable: "Lad brugerne skifte deres e-mail-adresse efter oprettelsen." + digest_topics: "Maximalt antal af populære emner vist i mails med sammendrag." + digest_posts: "Maximalt antal af populære indlæg vist i mails med sammendrag." + digest_other_topics: "Maximalt antal emner vist i 'Nyt i emner og kategorier du følger'-sektionen i mails med sammendrag." + digest_min_excerpt_length: "Minimumslængde for uddrag af indlæg i mails med sammendrag. Antal tegn." + suppress_digest_email_after_days: "Udelad mails med sammendrag til brugere, som ikke har besøgt siden i mere end (n) dage." + digest_suppress_categories: "Udelad disse kategorier fra mails med sammendrag" + disable_digest_emails: "Deaktiver mails med sammendrag for alle brugere." enable_user_directory: "Gør det muligt at gennemse et indeks af brugere " allow_anonymous_posting: "Tillad brugere at skifte til en anonym tilstand" allow_profile_backgrounds: "Tillad brugere at uploade en profil baggrund." @@ -857,8 +923,11 @@ da: invites_per_page: "Standard invitationer vist på en brugers side." short_progress_text_threshold: "Når antallet af indlæg overstiger dette tal viser statuslinjen kun det aktuelle indlægsnummer. Hvis du ændrer bredden af statuslinjen kan det være nødvendigt at opdatere denne værdi." default_code_lang: "Standard syntax highlighting som bruges i GitHub kodeblokke (lang-auto, ruby, python etc.)." + notify_about_flags_after: "Hvis der er flagmarkeringer, som ikke er blevet hånderet efter dette antal timer, så send en email til contact_email. Sæt 0 for at deaktivere." enable_emoji: "Aktivér emojis" - emoji_set: "Hvordan kunne du tænke dig dit emotikon?" + emoji_set: "Hvordan kunne du tænke dig din emoji?" + default_email_digest_frequency: "Hvor ofte brugere som standard modtager mails med sammendrag." + default_include_tl0_in_digests: "Inkluder som standard indlæg fra nye brugre i mails med sammendrag. Brugere kan ændre dette i deres indstillinger." default_email_private_messages: "Send som standard en email når nogen sender brugeren en besked." default_email_direct: "Send som standard en email når nogen citerer/svarer/nævner/inviterer brugeren." default_email_mailing_list_mode: "Send som standard en email for hvert nyt indlæg." @@ -873,16 +942,20 @@ da: default_categories_tracking: "Liste over kategorier der som standard følges." default_categories_muted: "Liste over kategorier der som standard ignoreres." errors: + invalid_email: "Ugyldig e-mail-adresse" + invalid_username: "Der er ingen bruger med det brugernavn." + invalid_integer_min_max: "Værdien skal være mellem %{min} og %{max}." invalid_integer_min: "Værdien skal være %{min} eller højere." invalid_integer_max: "Værdien kan ikke være højere end %{max}." invalid_integer: "Værdien skal være et heltal." regex_mismatch: "Værdien passer ikke til det påkrævede format." must_include_latest: "Topmenuen skal inkludere \"Latest\" fanen." invalid_string: "Ugyldig værdi." - invalid_string_min_max: "Skal være mellem %{min} og %{max} karakterer." - invalid_string_min: "Skal være mindst %{min} karakterer." - invalid_string_max: "Må ikke være mere end %{max} karakterer." + invalid_string_min_max: "Skal være mellem %{min} og %{max} tegn." + invalid_string_min: "Skal være mindst %{min} tegn." + invalid_string_max: "Må ikke være mere end %{max} tegn." search: + within_post: "#%{post_number} af %{username}" types: category: 'Kategorier' topic: 'Resultater' @@ -900,28 +973,51 @@ da: archived_disabled: "Dette emne er nu ikke længere arkiveret. Det er ikke længere frosset fast, og kan ændres." closed_enabled: "Dette emne er nu lukket. Nye indlæg tillades ikke." closed_disabled: "Dette emne er nu åbent. Nye indlæg tillades." + autoclosed_enabled_days: + one: "Dette emne blev automatisk lukket efter 1 dag. Nye svar er ikke længere tilladt." + other: "Dette emne blev automatisk lukket efter %{count} dage. Nye svar er ikke længere tilladt." + autoclosed_enabled_hours: + one: "Dette emne blev automatisk lukket efter 1 time. Nye svar er ikke længere tilladt." + other: "Dette emne blev automatisk lukket efter %{count} timer. Nye svar er ikke længere tilladt." + autoclosed_enabled_minutes: + one: "Dette emne blev automatisk lukket efter 1 minut. Nye svar er ikke længere tilladt." + other: "Dette emne blev automatisk lukket efter %{count} minutter. Nye svar er ikke længere tilladt." + autoclosed_enabled_lastpost_days: + one: "Dette emne blev automatisk lukket 1 dag efter det seneste svar. Nye svar er ikke længere tilladt." + other: "Dette emne blev automatisk lukket %{count} dage efter det seneste svar. Nye svar er ikke længere tilladt." + autoclosed_enabled_lastpost_hours: + one: "Dette emne blev automatisk lukket 1 time efter det seneste svar. Nye svar er ikke længere tilladt." + other: "Dette emne blev automatisk lukket %{count} timer efter det seneste svar. Nye svar er ikke længere tilladt." + autoclosed_enabled_lastpost_minutes: + one: "Dette emne blev automatisk lukket 1 minut efter det seneste svar. Nye svar er ikke længere tilladt." + other: "Dette emne blev automatisk lukket %{count} minutter efter det seneste svar. Nye svar er ikke længere tilladt." autoclosed_disabled: "Dette emne er nu åbnet. Nye svar er tilladt." autoclosed_disabled_lastpost: "Dette emne er nu åbent. Nye svar er tilladt." pinned_enabled: "Dette emne er nu fastgjort. Det vil stå i toppen af dets kategori indtil det bliver frigjort af \"staff\" for alle eller af hver bruger individuelt for dem selv." pinned_disabled: "Dette emne er nu frigjort. Det optræder ikke længere i toppen af dets kategori." + pinned_globally_enabled: "Emnet er nu fastgjort globalt. Det bliver vist i toppen af kategorien og i alle emnelister indtil hjælperteamet har frigjort det for alle eller individuelle brugere selv har frigjort det." + pinned_globally_disabled: "Dette emne er nu frigjort. Det ses ikke længere i toppen af kategorien." + visible_enabled: "Dette emne er nu synligt. Det vises i emnelister." + visible_disabled: "Dette emne er nu usynligt. Det vises ikke længere på emnelister. Den eneste måde at tilgå dette emne er via et direkte link." login: not_approved: "Din konto er ikke blevet godkendt af en moderator endnu. Du får en e-mail når den bliver godkendt." incorrect_username_email_or_password: "Ugyldigt navn, e-mail eller adgangskode" wait_approval: "Tak for din tilmelding. Du får besked, når din konto er blevet godkendt." active: "Din konto er aktiv og klar." - activate_email: "

    Du er næsten færdig! Vi har sendt en aktiverings-mail til %{email}. Følg venligst instruktionerne i den for at aktivere din konto.

    Hvis du ikke modtager den, skal du kigge i din spam-mappe eller få tilsendt en ny aktiverings.

    " not_activated: "Du kan ikke logge ind endnu. Vi har sendt en aktiverings-e-mail til dig. Følg venligst instruktionerne i e-mail’en for at aktivere din konto." suspended: "Du kan ikke logge ind før %{date}." errors: "%{errors}" not_available: "Ikke ledigt. Prøv %{suggestion}?" something_already_taken: "Noget gik galt, måske er brugernavnet eller e-mail-adressen allerede registreret. Prøv “Glemt adgangskode“-linket." omniauth_error_unknown: "Noget gik galt ved behandlingen af dit login, prøv venligst igen." - password_too_long: "Adgangskoder er begrænset til 200 karakterer." + password_too_long: "Kodeord er begrænset til 200 tegn." reserved_username: "Dette brugernavn er ikke tilladt." missing_user_field: "Du har ikke udfyldt alle felterne" close_window: "Godkendelsen er færdig. Luk dette vindue for at fortsætte." user: no_accounts_associated: "Ingen konti tilknyttet" + deactivated_by_staff: "Deaktiveret af hjælperteamet" + activated_by_staff: "Aktiveret af hjælperteamet" username: short: "skal være på mindst %{{min}} tegn." long: "skal være kortere end %{max} tegn" @@ -937,17 +1033,96 @@ da: blocked: "er ikke tilladt." ip_address: blocked: "Nye registreringer er ikke tilladt fra din IP adresse." + max_new_accounts_per_registration_ip: "Nye kontoer kan ikke oprettes fra din IP-adresse (max antal er nået). Kontakt et medlem af hjælperteamet." flags_reminder: + flags_were_submitted: + one: "Flagmarkeringerne blev indsendt for over 1 time siden. Gennemgå dem venligst." + other: "Flagmarkeringerne blev indsendt for over %{count} timer siden. Gennemgå dem venligst." subject_template: one: "1 flag venter på at blive håndteret" other: "%{count} flag venter på at blive håndteret" unsubscribe_mailer: subject_template: "Bekræft at du ikke længere ønsker at modtage email opdateringer fra %{site_title}" + flag_reasons: + off_topic: "Dit indlæg blev flagmarkeret som **off-topic**: Fællesskabet synes ikke, at det passer godt ind i emnet, som er blevet defineret af titlen og det første indlæg." + inappropriate: "Dit indlæg blev markeret som **upassende**: Fællesskabet synes, at det er stødende, groft eller krænker [vores fællesskabs retningslinier](/guidelines)." + spam: "Dit indlæg blev markeret som **spam**: Fællesskabet synes, at det er en reklame, noget der er overdrevent salgsfremmende i stedet for at være brugbart eller relevant for emnet." + notify_moderators: "Dit indlæg var markeret **til moderation**: Fællesskabet synes, at noget ved indlægget kræver ingriben af et medlem af hjælperteamet." + temporarily_closed_due_to_flags: + one: "Emnet er lukket i 1 time pga. et stort antal flagmarkeringer." + other: "Emnet er lukket i %{count} timer pga. et stort antal flagmarkeringer." system_messages: + post_hidden: + subject_template: "Inlægget er skjult pga. flagmarkeringer" welcome_user: subject_template: "Velkommen til %{site_name}!" welcome_invite: subject_template: "Velkommen til %{site_name}!" + csv_export_failed: + text_body_template: "Desværre, din dataeksport mislykkedes. Kig venligst i loggen eller kontakt hjælperteamet." + email_reject_insufficient_trust_level: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Din konto har ikke det nødvendige tillidsniveau til at kunne oprette nye emner til denne emailadresse. Hvis du mener, at det er en fejl, så kontakt hjælperteamet. + email_reject_user_not_found: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Dit svar blev sendt fra en ukendt emailadresse. Prøv at sende fra en anden emailadresse eller kontakt hjælperteamet. + email_reject_screened_email: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Dit svar blev sendt fra en blokeret emailadresse. Prøv at sende fra en anden emailadresse eller kontakt hjælperteamet. + email_reject_reply_user_not_matching: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Dit svar blev sendt fra en anden emailadresse end den forventede, så vi er ikke sikre på, at det er den samme person. Prøv at sende fra en anden emailadresse eller kontakt hjælperteamet. + email_reject_no_account: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Vi kan ikke finde nogen konto, der matcher din emailadresse. Prøv at sende fra en anden emailadresse eller kontakt hjælperteamet. + email_reject_invalid_access: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Din konto har ikke tilladelse til at oprette nye emner i kategorien. Hvis du mener, at det er en fejl, så kontakt hjælperteamet. + email_reject_strangers_not_allowed: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Kategorien du sendte mailen til tillader kun svar fra brugere med gyldige kontoer og kendte emailadresser. Hvis du mener, at det er en fejl, så kontakt hjælperteamet. + email_reject_reply_key: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Svarknappen i emailen er ugyldig eller ukendt, så vi ved ikke, hvad denne email er et svar til. Kontakt hjælpeteamet. + email_reject_bad_destination_address: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Ingen af modtageradresserne kan genkendes. Dobbelttjek at du sender til den korrekte emailadresse fra hjælperteamet. + email_reject_topic_not_found: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Emnet du besvarer eksisterer ikke længere - Det er måske slettet? Hvis du mener, at det er en fejl, så kontakt hjælperteamet. + email_reject_topic_closed: + text_body_template: | + Desværre, din mail til %{destination} (titled %{former_title}) virkede ikke. + + Emnet du besvarer er i øjeblikket lukket og modtager ikke længere svar. Hvis du mener, at det er en fejl, så kontakt hjælperteamet. + too_many_spam_flags: + title: "For Mange Flagmarkeringer for Spam" + too_many_tl3_flags: + title: "For Mange TL3 Flagmarkeringer" + blocked_by_staff: + title: "Blokeret af hjælperteamet" + user_automatically_blocked: + subject_template: "Den nye bruger %{username} er blokeret pga. flagmakeringer" pending_users_reminder: subject_template: one: "1 bruger afventer godkendelse" @@ -973,6 +1148,124 @@ da: reply_by_email: "[Besøg emnet](%{base_url}%{url}) eller svar på denne email for at svare." reply_by_email_pm: "[Gå til besked](%{base_url}%{url}) eller svar på denne email for at svare." posted_by: "Oprettet af %{username} den %{post_date}" + user_invited_to_private_message_pm: + subject_template: "[%{site_name}] %{username} inviterede dig til en privat besked '%{topic_title}'" + text_body_template: | + %{header_instructions} + + %{message} + + %{respond_instructions} + user_invited_to_private_message_pm_staged: + subject_template: "[%{site_name}] %{username} inviterede dig til en privat besked '%{topic_title}'" + text_body_template: | + %{header_instructions} + + %{message} + + %{respond_instructions} + user_invited_to_topic: + text_body_template: | + %{header_instructions} + + %{message} + + %{respond_instructions} + user_replied: + subject_template: "[%{email_prefix}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_replied_pm: + subject_template: "[%{email_prefix}] [PM] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_quoted: + subject_template: "[%{email_prefix}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_linked: + subject_template: "[%{email_prefix}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_mentioned: + subject_template: "[%{email_prefix}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_group_mentioned: + subject_template: "[%{email_prefix}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_posted: + subject_template: "[%{email_prefix}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_watching_first_post: + subject_template: "[%{email_prefix}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_posted_pm: + subject_template: "[%{email_prefix}] [PM] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + %{respond_instructions} + user_posted_pm_staged: + subject_template: "%{optional_re}%{topic_title}" + text_body_template: |2 + + %{message} digest: why: "Et kort resumé af %{site_link} siden dit sidste besøg %{last_seen_at}" since_last_visit: "Siden dit sidste besøg" @@ -980,37 +1273,30 @@ da: new_posts: "Nye indlæg" popular_topics: "Populære emner" join_the_discussion: "Læs mere" + subject_template: "[%{email_prefix}] Sammendrag" unsubscribe: "Dette resumé sendes fra %{site_link} når vi ikke har set dig i et stykke tid. For at afmelde %{unsubscribe_link}." click_here: "klik her" from: "%{site_name} resumé" preheader: "Et kort resume siden de sidste besøg %{last_seen_at}" - forgot_password: - text_body_template: | - Nogen har bedt om at få nulstillet dit kodeord på [%{site_name}](%{base_url}). - - Hvis det ikke var dig, så kan du trygt ignorere denne e-mail. - - Klik her for at vælge et nyt kodeord: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Nogen har bedt om at tilføje et kodeord til din konto på [%{site_name}](%{base_url}). - Du kan også loge ind med en af de undestøttede online services (Google, Facebook o.s.v. ) som associeret med denne bekræftede emailadresse. - - Hvis ikke du har bedt om dette kan du blot ignorere denne email. - - Klik på følgende for at vælge et kodeord.: - %{base_url}/users/password-reset/%{email_token} + mailing_list: + subject_template: "[%{email_prefix}] Summary for %{date}" + unsubscribe: "Opsummeringen sendes dagligt, fordi du er tilmeldt mailingslister i dine indstillinger. Afmelding: %{unsubscribe_link}." + from: "%{site_name} sammendrag" + new_topics: "Nye emner" + admin_login: + subject_template: "[%{email_prefix}] Login" + account_created: + subject_template: "[%{email_prefix}] Din Nye Konto" + confirm_new_email: + subject_template: "[%{email_prefix}] Bekræft din nye e-mail-adresse" + confirm_old_email: + subject_template: "[%{email_prefix}] Bekræft din nuværende e-mail-adresse" + notify_old_email: + subject_template: "[%{email_prefix}] Din e-mail-adresse er ændret" signup_after_approval: subject_template: "Du er blevet godkendt på %{site_name}!" signup: - text_body_template: | - Velkommen til %{site_name}! - - Klik på det følgende link for at bekræfte og aktivere din nye konto: - %{base_url}/users/activate-account/%{email_token} - - Hvis linket ikke virker, så prøv at kopiere hele linket ind i adressefeltet på din web browser. + subject_template: "[%{email_prefix}] Bekræft din nye konto" page_not_found: title: "Ups! Den side eksisterer ikke eller også er den privat." popular_topics: "Populære" @@ -1031,6 +1317,8 @@ da: images: too_large: "Beklager, men billedet som du forsøger at uploade er for stort (den maksimale størrelse er %{max_size_kb}KB). Gør det venligst mindre og prøv igen." size_not_found: "Beklager, men vi kunne ikke fastslå billedets størrelse. Måske er dit billede ødelagt?" + flag_reason: + sockpuppet: "En ny bruger oprettede et emne, og en anden ny bruger med den samme IP-adresse (%{ip_address}) besvarede. Se indstillinger for `flag_sockpuppets`." email_log: post_user_deleted: "Brugeren af dette svar er blevet slettet." no_user: "Kan ikke finde brugeren med id %{user_id}" @@ -1083,19 +1371,78 @@ da: name: Selvbiograf anniversary: name: Jubilæum + description: Aktivt medlem i et år, som har skrevet mindst et indlæg nice_post: name: Fint svar description: Modtog 10 likes på et svar good_post: name: Godt svar + description: Modtog 25 likes på et svar great_post: name: Rigtigt godt svar nice_topic: name: Fint emne + description: Modtog 10 likes på et emne good_topic: name: Godt emne + good_share: + description: Delte et indlæg med 300 unikke besøgende + first_like: + name: Første like + description: Synes om et indlæg + first_flag: + name: Første Flagmarkering + description: Flagmarkerede et indlæg + long_description: | + Dette badge bliver tildelt den første gang du flagmarkerer et indlæg. Ved at flagmakere hjælper vi alle med at sørge for, at forummet er et godt og rart sted for alle. Hvis du lægger mærke til poster, som kræver moderatorernes opmærksomhed, så tøv ikke med at flagmarkere. Du kan også markere for at sende en personlig besked til andre brugere, hvis du kan se et problem med deres indlæg. Hvis du ser et problem, så :flag_black: flagmarker det! + promoter: + description: Inviterede en bruger + campaigner: + description: Inviterede 3 basis brugere + champion: + name: Champion + description: Inviterede 5 medlemmer + first_share: + name: Første deling + description: Delte et indlæg + first_link: + name: Første link + description: Tilføjede et link til et andet emne + first_quote: + name: Første citering + description: Citerede et indlæg + read_guidelines: + name: Læst retningslinierne + description: Har læst retningslinierne for forummet + reader: + name: Læser + description: Har læst hvert eneste svar i et emne med mere end 100 svar + popular_link: + name: Populært link + description: Postede et eksternt link med 50 klik + long_description: | + Dette badge bliver tildelt, når et link du har delt er blevet klikket på 50 gange. Tak fordi du delte et brugbart link, som tilføjede interessant indhold til samtalen. + appreciated: + name: Værdsat + description: Modtog 1 like på 20 indlæg + long_description: | + Dette badge bliver tildelt, når du har modtaget mindst et like på 20 forskellige indlæg. Fællesskabet sætter pris på dine bidrag til samtalerne. + respected: + name: Respekteret + description: Modtog 2 likes på 100 indlæg + long_description: | + Dette badge bliver tildelt, når du har modtaget mindst 2 likes på 100 forskellige indlæg. Fællesskabet har voksende respekt for dine mange bidrag til samtalerne. admired: name: Beundret + description: Modtog 5 likes på 300 indlæg + long_description: | + Dette badge bliver tildelt, når du har modtaget mindst 5 likes på 300 forskellige indlæg. Vild! Fællesskabet beundrer din hyppige, gode bidrag til samtalerne. + out_of_love: + name: Ren kærlighed + description: Tildelt 50 likes på en dag + higher_love: + name: Mere kærlighed + description: Tildelt 50 likes på en dag 5 gange crazy_in_love: name: Vildt forelsket thank_you: @@ -1105,11 +1452,10 @@ da: empathetic: name: Empatisk description: Har 500 likede indlæg og givet 1000 likes - first_mention: - description: Nævnte en bruger i et indlæg + first_onebox: + long_description: Dette badge bliver tildelt første gang du poster et link i en linie for sig selv. Det vil automatisk ekspandere til en boks med et kort sammendrag af siden, en titel og (når muligt) et billede. first_reply_by_email: name: Første svar via email - description: Svarede på et indlæg via email long_description: | Dette emblem gives første gang du svarer til et indlæg via email :e-mail:. admin_login: @@ -1127,6 +1473,43 @@ da: staff_tag_disallowed: "Tagget \"%{tag}\" kan kun tildeles af personalet." staff_tag_remove_disallowed: "Tagget \"%{tag}\" kan kun fjernes af personalet." rss_by_tag: "Emner tagget %{tag}" + wizard: + step: + forum_title: + fields: + title: + label: "Dit fællesskabs navn" + placeholder: "Janes Sted" + site_description: + label: "Beskriv dit fællesskab med en kort sætning" + placeholder: "Et sted hvor Jane og hendes venner kan diskutere fede emner" + introduction: + title: "Introduktion" + fields: + welcome: + one_paragraph: "Begræns venligst din velkomstbesked til et afsnit." + privacy: + title: "Adgang" + fields: + privacy: + choices: + open: + label: "Offentlig" + description: "Alle har adgang til dette fællesskab og kan oprette en konto" + restricted: + label: "Privat" + contact: + fields: + contact_url: + label: "Hjemmeside" + placeholder: "http://www.eksempel.com/kontakt" + homepage: + title: "Hjemmeside" + emoji: + title: "Emoji" + invites: + title: "Inviter hjælpere" + description: "Du er næsten færdig! Lad os invitere nogle til hjælpereteamet, som kan href='https://blog.discourse.org/2014/08/building-a-discourse-community/' target='blank'>starte debatter med interessante emner og svar for at få gang i dit fællesskab." activemodel: errors: <<: *errors diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index 1df24e53f9..bdb6ebb192 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -27,7 +27,6 @@ de: topics: "Themen" posts: "Beiträge" loading: "Wird geladen" - powered_by_html: 'Basiert auf Discourse, funktioniert am besten mit aktiviertem JavaScript' log_in: "Anmelden" purge_reason: "Verlassenes, deaktiviertes Konto automatisch gelöscht" disable_remote_images_download_reason: "Der Download von Bildern wurde deaktiviert, weil nicht mehr genug Plattenplatz vorhanden war." @@ -110,7 +109,6 @@ de: s3_upload_bucket_is_required: "Uploads auf Amazon S3 können nicht aktiviert werden, bevor der 's3_upload_bucket' eingetragen wurde." invite: not_found: "Dein Einladungsschlüssel ist ungültig. Bitte kontaktiere den Administrator der Seite." - user_exists: "Es ist nicht nötig %{email} einzuladen, weil es dieses Konto bereits gibt!" bulk_invite: file_should_be_csv: "Die hochzuladende Datei sollte im CSV-Format vorliegen." error: "Es gab einen Fehler beim Hochladen dieser Datei. Bitte versuche es später noch einmal." @@ -273,14 +271,6 @@ de: Das Thema ist ohne Frage wichtig für dich – du hast hier mehr als %{percent}% der Antworten geschrieben. Bist du dir sicher, dass du anderen Leuten angemessen viel Raum lässt, auch ihre eigenen Punkte mitzuteilen? - get_a_room: | - ### Denke darüber nach, weiteren Personen zu antworten - - Du hast in diesem Thema bereits %{count}-mal auf @%{reply_username} geantwortet. - - Hast du schon darüber nachgedacht, auch *anderen* Personen in in der Diskussion zu antworten? Eine großartige Diskussion bezieht viele Stimmen und Perspektiven ein. - - Wenn du dein Gespräch mit dieser bestimmten Person ausführlich fortsetzen möchtest, [schicke ihr eine persönliche Nachricht](/users/%{reply_username}). too_many_replies: | ### Du hast das Antwort-Beschränkung für dieses Thema erreicht @@ -330,9 +320,6 @@ de: attributes: hex: invalid: "ist keine gültige Farbe" - post_reply: - base: - different_topic: "Beitrag und Antwort müssen zum gleichen Thema gehören." web_hook: attributes: payload_url: @@ -343,7 +330,6 @@ de: taken: wird schon von einem anderen Emoji verwendet <<: *errors user_profile: - no_info_me: "
    Das 'Über mich'-Feld deines Profils ist aktuell leer, möchtest du es ausfüllen?
    " no_info_other: "
    %{name} hat noch nichts in das 'Über mich'-Feld des Benutzerprofils eingetragen
    " vip_category_name: "Lounge" vip_category_description: "Eine Kategorie exklusiv für Mitglieder mit Vertrauensstufe 3 oder höher." @@ -1411,7 +1397,6 @@ de: incorrect_username_email_or_password: "Benutzername, E-Mail-Adresse oder Passwort falsch" wait_approval: "Danke fürs Registrieren. Wir werden dich benachrichtigen, sobald dein Benutzerkonto genehmigt wurde." active: "Dein Konto ist nun freigeschaltet und einsatzbereit." - activate_email: "

    Fast fertig! Eine E-Mail mit einem Aktivierungscode wurde an deine E-Mail-Adresse %{email} gesendet.
    Dort findest du die Anleitung, um deinen Zugang zu aktivieren.

    Solltest du die E-Mail nicht vorfinden, kontrolliere bitte auch den Spam-Ordner. Gegebenenfalls kannst du dir von hier eine weitere Aktivierungs-E-Mail zusenden lassen.

    " not_activated: "Du kannst dich noch nicht anmelden. Wir haben dir eine Aktivierungs-E-Mail geschickt. Bitte folge zunächst den Anweisungen in dieser E-Mail, um dein Konto zu aktivieren." not_allowed_from_ip_address: "Du kannst dich von dieser IP-Adresse aus nicht als %{username} anmelden." admin_not_allowed_from_ip_address: "Du kannst dich von dieser IP-Adresse aus nicht als Administrator anmelden." @@ -1526,7 +1511,6 @@ de: %{invite_link} custom_invite_forum_mailer: - title: "Einladung zur Teilnahme" subject_template: "%{invitee_name} hat dich eingeladen %{site_domain_name} beizutreten" text_body_template: | %{invitee_name} hat dich eingeladen der folgenden Seite beizutreten: @@ -1545,13 +1529,6 @@ de: invite_password_instructions: title: "Passwort festlegen" subject_template: "Lege ein Passwort für dein %{site_name}-Konto fest" - text_body_template: | - Schön, dass du die Einladung zu %{site_name} angenommen hast -- Willkommen! - - Klicke hier um jetzt dein Passwort festzulegen: - %{base_url}/users/password-reset/%{email_token} - - (Wenn der Link abgelaufen ist, wähle "Passwort vergessen" aus, wenn du dich mit deiner E-Mail-Adresse einloggen möchtest.) download_backup_mailer: title: "Backup-Download Mailer" subject_template: "[%{email_prefix}] Backup der Seite herunterladen" @@ -1659,7 +1636,6 @@ de: disagreed: "Danke für deine Meldung. Wir sehen uns das an." deferred: "Danke für deine Meldung. Wir sehen uns das an." deferred_and_deleted: "Danke für deine Meldung. Wir haben den Beitrag entfernt." - temporarily_closed_due_to_flags: "Dieses Thema ist vorrübergehend geschlossen. Mehrere User haben dieses Thema gemeldet. " system_messages: post_hidden: title: "Benachrichtigung: Beitrag versteckt" @@ -1678,11 +1654,6 @@ de: Wenn der Beitrag jedoch von der Community erneut ausgeblendet wurde, wird er ausgeblendet bleiben, bis sich das Team darum kümmert – und möglicherweise gibt es weitere Maßnahmen, einschließlich einer möglichen Sperrung deines Kontos. Weitre Hinweise findest du in unseren [Community-Richtlinien](%{base_url}/guidelines). - usage_tips: - text_body_template: | - Ein paar Tipps für die ersten Schritte als neuer Benutzer [findest du in diesem Blog-Eintrag](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). - - Während du hier teilnimmst, werden wir dich kennenlernen und die vorübergehenden Einschränkungen für neue Benutzer werden aufgehoben. Über die Zeit, erreichst du [Vertrauensstufen](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), die spezielle Fähigkeiten beinhalten, um die Community gemeinsam zu verwalten. welcome_user: title: "Willkommen: Benutzer" subject_template: "Willkommen bei %{site_name}!" @@ -2231,57 +2202,21 @@ de: forgot_password: title: "Passwort vergessen" subject_template: "[%{email_prefix}] Passwort zurücksetzen" - text_body_template: | - Jemand hat darum gebeten, dein Passwort auf [%{site_name}](%{base_url}) zurückzusetzen. - - Wenn du es nicht selbst warst, kannst du diese E-Mail einfach ignorieren. - - Andernfalls besuche den folgenden Link, um ein neues Passwort zu wählen: - %{base_url}/users/password-reset/%{email_token} set_password: title: "Passwort setzen" subject_template: "[%{email_prefix}] Passwort wählen" - text_body_template: | - Jemand hat ein Passwort für dein Konto auf [%{site_name}](%{base_url}) beantragt. Alternativ kannst du dich mit einem unterstützen Online-Service (Google, Facebook, etc), der mit dieser validierten E-Mail-Adresse verknüpft ist, anmelden. - - Wenn diese Anfrage nicht von dir kam, kannst du diese E-Mail einfach ignorieren. - - Klicke auf den folgenden Link, um ein Passwort auszuwählen: - %{base_url}/users/password-reset/%{email_token} admin_login: title: "Administrator-Anmeldung" subject_template: "[%{email_prefix}] Anmeldung" - text_body_template: | - Jemand hat versucht sich in dein Konto auf [%{site_name}](%{base_url}) anzumelden. - - Wenn dieser Anmeldeversuch nicht von dir stammt, kannst du diese E-Mail ignorieren. - - Klicke den folgenden Link, um dich anzumelden: - %{base_url}/users/admin-login/%{email_token} account_created: title: "Konto erstellt" subject_template: "[%{email_prefix}] Dein neues Konto" - text_body_template: | - Ein neues Konto wurde für dich auf %{site_name} erstellt. - - Klicke auf den folgenden Link, um ein Passwort für dein neues Konto festzulegen: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "E-Mail-Adresse bestätigen (an neue)" subject_template: "[%{email_prefix}] Bestätige deine neue E-Mail-Adresse" - text_body_template: | - Um deine E-Mail-Adresse für %{site_name} zu bestätigen, klicke auf den folgenden Link: - - %{base_url}/users/authorize-email/%{email_token} confirm_old_email: title: "E-Mail-Adresse bestätigen (an alte)" subject_template: "[%{email_prefix}] Bestätige deine aktuelle E-Mail-Adresse" - text_body_template: | - Bevor wir deine E-Mail-Adresse ändern können, müssen wir dich bitten zu bestätigen, dass du das E-Mail-Konto kontrollierst. Nach diesem Schritt, werden wir dich bitten die neue E-Mail-Adresse zu bestätigen. - - Um deine E-Mail-Adresse für %{site_name} zu bestätigen, klicke auf den folgenden Link: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: title: "Benachrichtigung an alte E-Mail-Adresse" subject_template: "[%{email_prefix}] Deine E-Mail-Adresse wurde geändert" @@ -2294,33 +2229,9 @@ de: signup_after_approval: title: "Konto bestätigen nach Genehmigung" subject_template: "You've been approved on %{site_name}!" - text_body_template: | - Willkommen bei %{site_name}! - - Einer unserer Team-Mitglieder hat dein Benutzerkonto bei %{site_name} genehmigt. - - Klicke auf den folgenden Link, um dein neues Konto zu bestätigen und zu aktivieren: - %{base_url}/users/activate-account/%{email_token} - - Wenn der obige Link nicht klickbar ist, kopiere ihn in die Adresszeile deines Web-Browsers. - - %{new_user_tips} - - Wir wünschen uns ein stets [zivilisiertes Verhalten in unserer Community](%{base_url}/guidelines). - - Viel Spaß! - - (Wenn du als neuer Benutzer unter vier Augen mit einem unserer [Team-Mitglieder](%{base_url}/about) reden möchtest, antworte einfach auf diese Nachricht.) signup: title: "Konto bestätigen nach Anmeldung" subject_template: "[%{email_prefix}] Bestätige dein neues Konto" - text_body_template: | - Willkommen bei %{site_name}! - - Um dein Konto zu aktivieren, klicke auf den folgenden Link: - %{base_url}/users/activate-account/%{email_token} - - Wenn dieser Link nicht klickbar ist, kopiere ihn in die Adresszeile deines Web-Browsers. page_not_found: title: "Hoppla! Diese Seite existiert nicht oder ist privat." popular_topics: "Beliebt" @@ -2909,7 +2820,6 @@ de: Das Abzeichen wird verliehen, wenn du das erste Mal einen Emoji zu deinem Beitrag hinzufügst. :thumbsup: Emojis lassen dich Emotionen in deinen Beiträgen vermitteln, von Glück :smiley: zu Trauer :anguished: zu Wut :angry: und allem dazwischen :sunglasses:. Gib einfach einen : (Doppelpunkt) ein oder drücke die Emoji-Schaltfläche im Editor, um aus Hunderten Auswahlmöglichkeiten zu wählen :ok_hand: first_mention: name: Erste Erwähnung - description: Hat einen Benutzer in einem Beitrag erwähnt long_description: Das Abzeichen wird verliehen, wenn du das erste Mal den @Benutzernamen von jemandem in deinem Beitrag erwähnst. Jede Erwähnung erzeugt eine Benachrichtigung an die Person, sodass sie über deinen Beitrag informiert wird. Gib einfach ein @ (At-Zeichen) ein und erwähne einen beliebigen Benutzer oder, sofern erlaubt, eine Gruppe – es ist ein komfortabler Weg, die Aufmerksamkeit von jemandem auf etwas zu lenken. first_onebox: name: Erste Onebox @@ -2917,7 +2827,6 @@ de: long_description: Das Abzeichen wird verliehen, wenn du das erste Mal einen Link auf einer eigenen Zeile schreibst, der dann automatisch in eine Onebox umgewandelt wurde, mit einer kurzen Zusammenfassung des Links, einem Titel und einem Bild (falls verfügbar). first_reply_by_email: name: Erste Antwort per E-Mail - description: Hat auf einen Beitrag per E-Mail geantwortet. long_description: | Das Abzeichen wird verliehen, wenn du das erste Mal per E-Mail :e-mail: auf einen Beitrag antwortest. admin_login: @@ -2951,7 +2860,6 @@ de: safe_mode: title: "Abgesicherten Modus betreten" description: "Der abgesicherte Modus ermöglicht es dir, deine Seite zu testen, ohne Plugins oder Seiten-Anpassungen zu laden." - no_customizations: "Alle Seiten-Anpassungen deaktivieren" only_official: "Inoffizielle Plugins deaktivieren" no_plugins: "Alle Plugins deaktivieren" enter: "Abgesicherten Modus betreten" diff --git a/config/locales/server.el.yml b/config/locales/server.el.yml index 528616307a..397b742506 100644 --- a/config/locales/server.el.yml +++ b/config/locales/server.el.yml @@ -27,7 +27,6 @@ el: topics: "Νήματα" posts: "αναρτήσεις" loading: "Φόρτωση" - powered_by_html: 'Βασίζεται στο Discourse, λειτουργεί καλύτερα με ενεργοποιημένη τη Javascript' log_in: "Σύνδεση" purge_reason: "Διεγράφη αυτόματα ως εγκαταλελειμμένος, μη ενεργοποιημένος λογαριασμός." disable_remote_images_download_reason: "Το κατέβασμα εικόνων απενεργοποιήθηκε επειδή δεν υπάρχει αρκετός χώρος στο δίσκο." @@ -110,7 +109,6 @@ el: s3_upload_bucket_is_required: "Δεν μπορείτε να ενεργοποιήσετε μεταφορτώσεις στο S3 εάν δεν παρέχετε το 's3_upload_bucket'." invite: not_found: "Η πρόσκληση δεν είναι έγγυρη. Παρακαλώ επικοινωνήστε με τον διαχειρηστή της ιστοσελίδας." - user_exists: "Δεν χρειάζεται να προσκαλέσεις %{email}, αυτοί ήδη έχουν λογαριασμό!" bulk_invite: file_should_be_csv: "To ανεβασμένο αρχείο πρέπει να έχει μορφή csv." error: "Παρουσίαστηκε ένα σφάλμα κατά το ανέβασμα του αρχείου σας, Παρακαλώ δοκιμάστε αργότερα." @@ -273,14 +271,6 @@ el: Αυτό το νήμα φαίνεται να είναι σημαντικό για εσάς &ndash. Έχετε δημοσιεύσει περισσότερο από %{percent}% των απαντήσεων εδώ. Είστε σίγουρος ότι παρέχετε επαρκή χρόνο σε άλλους ανθρώπους να μοιραστούν κι εκείνοι τις δικές τους απόψεις; - get_a_room: | - ### Σκεφθείτε να απαντήσετε σε περισσότερους ανθρώπους - - Έχετε ήδη απαντήσει %{count} φορές σε @%{reply_username} σε αυτό το συγκεκριμένο νήμα. - - Έχετε σκεφθεί να απαντήσετε και σε *άλλους* ανθρώπους στη συζήτηση; Μια καλή συζήτηση εμπεριέχει πολλές φωνές και οπτικές γωνίες. - - Εάν θέλετε να συνεχίσετε τη συζήτησή σας με αυτόν τον συγκεριμένο χρήστη, [στείλτε τους ένα προσωπικό μήνυμα](/users/%{reply_username}). too_many_replies: | ### Έχετε φτάσει το όριο απαντήσεων γι' αυτό το νήμα @@ -330,16 +320,12 @@ el: attributes: hex: invalid: "δεν είναι ένα έγκυρο χρώμα" - post_reply: - base: - different_topic: "Η δημοσίευση και η απάντηση πρέπει να ανήκουν στο ίδιο νήμα." web_hook: attributes: payload_url: invalid: "Το URL δεν είναι έγκυρο. To URL πρέπει να συμπεριλαμβάνει http:// ή https://. Και δεν επιτρέπεται κανένα κενό." <<: *errors user_profile: - no_info_me: "
    το πεδίο Σχετικά Με Εμένα είναι κενό, θα ήθελες να το συμπληρώσεις?
    " no_info_other: "
    %{name} δεν έχει εισάγει τίποτα στο πεδίο Σχετικά με Εμένα στο προφίλ του
    " vip_category_name: "Σαλόνι" vip_category_description: "Κατηγορία αποκλειστικά για μέλη με επίπεδο εμπιστοσύνης (trust level) 3 ή υψηλότερο." @@ -1416,7 +1402,6 @@ el: incorrect_username_email_or_password: "Λάθος όνομα χρήστη, διεύθυνση ηλεκτρονικού ταχυδρομείου ή κωδικός προσβασης" wait_approval: "Ευχαριστούμε για την εγγραφή σας. Θα σας ενημερώσουμε όταν ο λογαριασμός σας εγκριθεί." active: "Ο λογαριασμός σας έχει ενεργοποιηθεί και είναι έτοιμος προς χρήση" - activate_email: "

    Είσαι σχεδόν έτοιμος! Έχουμε στείλει ένα ηλεκτρονικό μήνυμα ενεργοποίησης προς %{email}. Παρακαλώ, ακολούθησε τις οδηγίες που υπάρχουν στο ηλεκτρονικό μήνυμα για να ενεργοποιήσεις τον λογαριασμό σου.

    Εάν δεν το παραλάβεις, έλεγξε τον φάκελο με τα Ανεπιθύμητα, ή προσπάθησε να συνδεθείς ξανά για να σου στείλουμε ένα άλλο ηλεκτρονικό μήνυμα ενεργοποίησης.

    " not_activated: "Δεν μπορείς να συνδεθείς ακόμα. Σου έχουμε στείλει ένα ηλεκτρονικό μήνυμα ενεργοποίησης. Παρακαλώ, ακολούθησε τις οδηγίες που υπάρχουν στο μήνυμα για να ενεργοποιήσεις τον λογαριασμό σου." not_allowed_from_ip_address: "Δεν μπορείς να συνδεθείς ως %{username} από αυτή τη διεύθυνση IP." admin_not_allowed_from_ip_address: "Δεν μπορείς να συνδεθείς ως διαχειριστής από αυτή τη διεύθυνση IP." @@ -1532,7 +1517,6 @@ el: %{σύνδεσμος_πρόσκλησης} custom_invite_forum_mailer: - title: "Προσκαλέστε τον Μέιλερ του Φόρουμ" subject_template: "%{invitee_name} σε προσκάλεσε να συμμετέχεις στο %{site_domailn_name}" text_body_template: | %{όνομα_προσκεκλημένου} σας προσκάλεσε να γίνετε μέλος @@ -1553,14 +1537,6 @@ el: invite_password_instructions: title: "Προσκάλεσε οδηγίες κωδικού" subject_template: "Όρισε κωδικό πρόσβασης για τον λογαρισμό σου %{όνομα_ιστοσελίδας}" - text_body_template: |+ - Ευχαριστώ που δέχθηκες την προσκλησή σου στο %{site_name} -- Καλώς ήρθες! - - Πάτησε αυτόν τον σύνδεσμο για να επιλέξεις κωδικό τώρα: - %{base_url}/users/password-reset/%{email_token} - - (Εάν ο σύνδεσμος πιο πάνω έχει λήξει, επέλεξε το "Ξέχασα τον κωδικό μου" όταν θα συνδεθείς με τον ηλεκτρονικό σου λογαριασμό.) - test_mailer: title: "Δοκιμάστε τον αποστολέα" text_body_template: | @@ -1651,7 +1627,6 @@ el: disagreed: "Ευχαριστούμε που μας ειδοποιήσατε. Το εξετάζουμε." deferred: "Ευχαριστούμε που μας ειδοποιήσατε. Το εξετάζουμε." deferred_and_deleted: "Ευχαριστούμε που μας ειδοποίησες. Έχουμε αφαιρέσει τη δημοσίευση." - temporarily_closed_due_to_flags: "Αυτό το νήμα είναι προσωρινά κλειστό εξαιτίας του μεγάλου αριθμού επισημάνσεων της κοινότητας." system_messages: post_hidden: title: "Η δημοσίευση κρύφτηκε" @@ -1670,11 +1645,6 @@ el: Όμως, εάν η ανάρτηση αποκρυφθεί από την κοινότητα για δεύτερη φορά, θα παραμείνει κρυμμένη έως ότου διαχειριστεί απο συνεργάτη, και ίσως υπάρξουν περαιτέρω ενέργειες συμπεριλαμβανομένης την πιθανής διακοπής του λογαριασμού σου. Για επιπλεον καθοδήγηση, παρακαλούμε ανάτρεξε στις [οδηγίες κοινότητας](%{base_url}/guidelines). - usage_tips: - text_body_template: | - Για να βρεις μερικά μυστικά για να αρχίσεις ως νέος χρήστης, [τσέκαρε αυτή την ανάρτηση στο ιστολόγιο](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). - - Καθώς συμμετέχεις εδώ, θα σε γνωρίσουμε και οι προσωρινοί περιορισμοί νέου χρήστη θα αρθούν. Με τον καιρό θα αποκτάς [επίπεδα εμπιστοσύνης](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) που περιλαμβάνουν ειδικές δυνατότητες και μας βοηθούν να διαχειριζόμαστε την κοινότητα μαζί. welcome_user: title: "Καλώς ήλθες χρήστη" subject_template: "Καλώς ήλθατε στο %{site_name}!" @@ -2195,59 +2165,16 @@ el: back_to_top: "Πίσω στην κορυφή" forgot_password: title: "Ξέχασα τον κωδικό πρόσβασης" - text_body_template: |+ - Κάποιος ζήτησε να επαναφέρει τον κωδικό πρόσβασης σου στο [%{site_name}](%{base_url}). - - Εάν δεν ήσουν εσύ, μπορείς να αγνοήσεις αυτό το μήνυμα. - - Πάτησε τον ακόλουθο σύνδεσμο για να επιλέξεις νέο κωδικό: - %{base_url}/users/password-reset/%{email_token} - - - set_password: title: "Όρισε κωδικό πρόσβασης" - text_body_template: |+ - Κάποιος ζήτησε να προσθέσει έναν κωδικό στον λογαριασμό σου στο [%{site_name}](%{base_url}). - Εναλλακτικά, μπορείς να συνδεθείς χρησιμοποιώντας μια υποστηρίσημη υπηρεσία απευθείας σύνδεσης. (Google, Facebook, etc) που να σχετίζεται με αυτήν την επικυρωμένη ηλεκτρονιή διεύθυνση. - - Εάν δεν έκανες εσύ το αίτημα, μπορείς να αγνοήσεις αυτό το μήνυμα. - - Πάτησε τον ακόλουθο σύνδεσμο για να επιλέξεις κωδικό: - %{base_url}/users/password-reset/%{email_token} - admin_login: title: "Σύνδεση Διαχειριστή" - text_body_template: | - Κάποιος ζήτησε να συνδεθεί στο λογαριασμό σου στο [%{site_name}](%{base_url}). - - Εάν δεν έκανες εσύ αυτή την ενέργεια, μπορείς να αγνοήσεις αυτό το email με ασφάλεια. - - Πάτησε τον ακόλουθο σύνδεσμο για να συνδεθείς: - %{base_url}/users/admin-login/%{email_token} account_created: title: "Ο λογαριασμός δημιουργήθηκε" - text_body_template: | - Ένας καινούριος λογαριασμός δημιουργήθηκε για εσένα στο %{site_name} - - Κάνε κλικ στον ακόλουθο σύνδεσμο να επιλέξεις κωδικό πρόσβασης για τον καινούριο σου λογαριασμό: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "Επιβεβαίωση Νέας Ηλεκτρονικής Διεύθυνσης" - text_body_template: | - Επιβεβαίωσε την καινούρια διεύθυνση ηλεκτρονικού ταχυδρομείου για %{site_name} κάνοντας κλικ στον ακόλουθο σύνδεσμο: - - %{base_url}/users/authorize-email/%{email_token} confirm_old_email: title: "Επιβεβαίωση Παλιάς Ηλεκτρονικής Διεύθυνσης" - text_body_template: | - Πριν μπορέσομε να αλλάξουμε την διεύθυνση ηλεκτρονικού ταχυδρομείου σου, χρειαζόμαστε επιβεβαίωση ότι ελέγχεις - την τωρινή διεύθυνση ηλεκτρονικού ταχυδρομείου. Μόλις ολοκληρώσεις αυτό το βήμα, θα σου επιτρέψουμε να επιβεβαιώσεις - τη νέα διεύθυνση ηλεκτρονικού ταχυδρομείου. - - Επιβεβαίωσε την τωρινή σου διεύθυνση ηλεκτρονικού ταχυδρομείου για το %{site_name} πατώντας στον ακόλουθο σύνδεσμο: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: title: "Ειδοποίηση παλιάς ηλεκτρονικής διεύθυνσης" text_body_template: | @@ -2262,32 +2189,8 @@ el: signup_after_approval: title: "Εγγραφή μετά την έγκριση " subject_template: "Έχετε εγκριθεί στο %{site_name}!" - text_body_template: | - Καλώς ήρθες στο %{site_name}! - - Ένα μέλος του προσωπικού ενέκρινε το λογαριασμό σου στο %{site_name}. - - Πάτησε τον ακόλουθο σύνδεσμο για να επιβεβαιώσεις και να ενεργοποιήσεις το νέο σου λογαριασμό: - %{base_url}/users/activate-account/%{email_token} - - Εάν δεν μπορείς να πατήσεις τον παραπάνω σύνδεσμο, δοκίμασε αντιγραφή κι επικόλλησή του στην μπάρα διευθύνσεων του περιηγητή σου. - - %{new_user_tips} - - Πιστεύουμε στην [civilized community behavior](%{base_url}/guidelines) πάντα. - - Απόλαυσε την παραμονή σου! - - (Αν χρειαστεί να επικοινωνήσεις με [staff members](%{base_url}/about) ως νέος χρήστης, απλώς απάντησε σε αυτό το μήνυμα.) signup: title: "Εγγραφή" - text_body_template: | - Καλώς ήρθες στο %{site_name}! - - Πάτησε τον ακόλουθο σύνδεσμο για να ςπιβεβιώσεις και να ενεργοποιήσεις το νέο σου λογαριασμό: - %{base_url}/users/activate-account/%{email_token} - - Εάν δεν μπορείς να πατήσεις τον παραπάνω σύνδεσμο, δοκίμασε αντιγραφή κι επικόλληση στην μπάρα διευθύνσεων του περιηγητή σου. page_not_found: title: "Ουπς! Αυτή η σελίδα δεν υπάρχει ή είναι ιδιωτική." popular_topics: "Δημοφιλή" @@ -2824,7 +2727,6 @@ el: Αυτό το παράσημο δίνεται την πρώτη φορά που προσθέτεις κάποιο Emoji σε ανάρτησή σου :thumbsup:. Τα Emoji σου επιτρέπουν να προσθέσεις συναίσθημα στις αναρτήσεις σου, από χαρά :smiley: έως λύπη :anguished: έως θυμό :angry: και όλα τα ενδιάμεσα :sunglasses: . Απλώς πληκτρολόγησε ένα : (colon) ή πίεσε το πλήκτρο εργαλειοθήκης Emoji στο συνθέτη για να διαλέξεις από εκατοντάδες επιλογές :ok_hand: first_mention: name: Πρώτη αναφορά - description: Ανέφερε ένα χρήστη στη δημοσίευση long_description: 'Αυτό το παράσημο δίνεται την πρώτη φορά που αναφέρεις το @χρηστώνυμο κάποιου στην ανάρτηση. Κάθε αναφορά δημιουργεί μια ειδοποίηση σε αυτό το άτομο, ώστε να μάθουν για την ανάρτησή σου. Απλώς ξεκίνα να πληκτρολογείς το @ (στα σύμβολα) για να αναφέρεις οποιδήποτε χρήστη ή, εάν επιτρέπεται, μια ομάδα. Είναι ένας βολικός τρόπος να τραβήξεις την προσοχή τους σε κάτι. ' first_onebox: name: Πρώτο Onebox @@ -2832,7 +2734,6 @@ el: long_description: 'Αυτό το παράσημο δίνεται την πρώτη φορά που αναρτείς ένα σύνδεσμο μόνο του σε μια γραμμή, το οποίο αυτόματα αναπτύσσεται σε onebox με μια σύντονη περίληψη του συνδέσμου, ένα τίτλο και (όπου αυτό είναι διαθέσιμο) μια εικόνα. ' first_reply_by_email: name: Πρώτη απάντηση μέσω ηλεκτρονικού ταχυδρομείου - description: Απάντησε σε μια Δημοσίευση μέσω του ηλεκτρονικού ταχυδρομείου long_description: | Το παράσημο χορηγείται την πρώτη φορά που απαντάς σε μια δημοσίευση μέσω ηλεκτρονικού ταχυδρομείου :ηλεκτρονική διεύθυνση:. admin_login: @@ -2866,7 +2767,6 @@ el: safe_mode: title: "Εισαγωγή λειτουργίας ασφαλείας" description: "Η ασφαλής λειτουργία σου επιτρέπει να εξετάσεις τον ιστότοπό σου χωρίς να φορτώσεις πρόσθετα ή εξηατομικεύσεις ιστοτόπου." - no_customizations: "Απενεργοποίησε όλες τις προσαρμογές του ιστότοπου. " only_official: "Απενεργοποίησε ανεπίσημα πρόσθετα" no_plugins: "Απενεργοποίησε όλα τα πρόσθετα" enter: "Εισαγωγή λειτουργίας ασφαλείας" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 2fe259ab23..8b851a1c77 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -47,7 +47,7 @@ en: topics: "Topics" posts: "posts" loading: "Loading" - powered_by_html: 'Powered by Discourse, best viewed with JavaScript enabled' + powered_by_html: 'Powered by Discourse, best viewed with JavaScript enabled' log_in: "Log In" purge_reason: "Automatically deleted as abandoned, deactivated account" @@ -55,6 +55,9 @@ en: anonymous: "Anonymous" remove_posts_deleted_by_author: "Deleted by author" + themes: + bad_color_scheme: "Can not update theme, invalid color scheme" + other_error: "Something went wrong updating theme" emails: incoming: default_subject: "This topic needs a title" @@ -252,6 +255,8 @@ en: delete_reason: "Deleted via post moderation queue" groups: + success: + bulk_add: "%{users_added} users have been added to the group." errors: can_not_modify_automatic: "You cannot modify an automatic group" member_already_exist: "'%{username}' is already a member of this group." @@ -1303,7 +1308,7 @@ en: reply_by_email_enabled: "Enable replying to topics via email." reply_by_email_address: "Template for reply by email incoming email address, for example: %{reply_key}@reply.example.com or replies+%{reply_key}@example.com" alternative_reply_by_email_addresses: "List of alternative templates for reply by email incoming email addresses. Example: %{reply_key}@reply.example.com|replies+%{reply_key}@example.com" - incoming_email_prefer_html: "Use the HTML instead of the text for incoming email. May cause unexpected formatting issues!" + incoming_email_prefer_html: "Use the HTML instead of the text for incoming email." disable_emails: "Prevent Discourse from sending any kind of emails" @@ -1334,6 +1339,7 @@ en: enable_forwarded_emails: "[BETA] Allow users to create a topic by forwarding an email in." always_show_trimmed_content: "Always show trimmed part of incoming emails. WARNING: might reveal email addresses." + private_email: "Don't include content from posts or topics in emails for extra privacy." manual_polling_enabled: "Push emails using the API for email replies." pop3_polling_enabled: "Poll via POP3 for email replies." @@ -1443,7 +1449,7 @@ en: embed_username_required: "The username for topic creation is required." embed_whitelist_selector: "CSS selector for elements that are allowed in embeds." embed_blacklist_selector: "CSS selector for elements that are removed from embeds." - notify_about_flags_after: "If there are flags that haven't been handled after this many hours, send an email to the contact_email. Set to 0 to disable." + notify_about_flags_after: "If there are flags that haven't been handled after this many hours, send a private message to staff. Set to 0 to disable." show_create_topics_notice: "If the site has fewer than 5 public topics, show a notice asking admins to create some topics." delete_drafts_older_than_n_days: Delete drafts older than (n) days. @@ -1932,6 +1938,9 @@ en: other: "This topic is temporarily closed for %{count} hours due to a large number of community flags." system_messages: + private_topic_title: "Topic #%{id}" + contents_hidden: "Please visit the post to see its contents." + post_hidden: title: "Post Hidden" subject_template: "Post hidden by community flags" @@ -1952,7 +1961,7 @@ en: usage_tips: text_body_template: | - For a few quick tips on getting started as a new user, [check out this blog post](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). + For a few quick tips on getting started as a new user, [check out this blog post](https://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). As you participate here, we’ll get to know you, and temporary new user limitations will be lifted. Over time you’ll gain [trust levels](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) that include special abilities to help us manage our community together. @@ -2763,6 +2772,8 @@ en: color_schemes: base_theme_name: "Base" + default: "Light Scheme" + dark: "Dark Scheme" about: "About" guidelines: "Guidelines" @@ -3359,7 +3370,7 @@ en: safe_mode: title: "Enter safe mode" description: "Safe mode allows you to test your site without loading plugins or site customizations." - no_customizations: "Disable all site customizations" + no_customizations: "Disable current theme" only_official: "Disable unofficial plugins" no_plugins: "Disable all plugins" enter: "Enter Safe Mode" diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index aff9a4d1eb..26005e77ba 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -27,7 +27,6 @@ es: topics: "Temas" posts: "posts" loading: "Cargando" - powered_by_html: 'Funciona gracias a Discourse, se ve mejor con JavaScript activado' log_in: "Iniciar sesión" purge_reason: "Cuenta eliminada automáticamente al detectarse como abandonada, desactivada" disable_remote_images_download_reason: "La descarga de imágenes remotas se desactivó porque no había suficiente espacio disponible en disco." @@ -110,6 +109,7 @@ es: s3_upload_bucket_is_required: "No se pueden activar las subidas a S3 a menos que se haya proporcionado un valor a 's3_upload_bucket'." invite: not_found: "El código de la invitación es inválido. Por favor, ponte en contacto con un administrador." + user_exists: "No hace falta invitar a %{email}, ¡ya tiene una cuenta!" bulk_invite: file_should_be_csv: "El archivo subido debería ser de formato csv." error: "Ha ocurrido un error al subir ese archivo. Por favor, inténtalo más tarde de nuevo." @@ -273,13 +273,13 @@ es: ¿Estás seguro de que estás proporcionando el tiempo adecuado para que otras personas puedan ofrecer también sus puntos de vista? get_a_room: | - ### Puedes responderles a otras personas + ### Considera responder a más gente - Has respondido %{count} veces a @%{reply_username} sólo en este tema. + Ya le has respondido %{count} veces a @%{reply_username} en este tema en particular. - Has considerado que puedes responder a las otras personas en el debate también? Una buena conversación incluye muchas respuestas y perspectivas. + ¿Has pensado en responder a *otra* gente en el tema también? Una buena conversación involucra a varias personas y perspectivas. - Si necesitas mantener la conversación con ese usuario en particular, puedes [enviarle un mensaje privado](/users/%{reply_username}). + Si quieres continuar la conversación con este usuario en particular por privado, [mándales un mensaje personal](/u/%{reply_username}). too_many_replies: | ### Has alcanzado el límite de respuestas para este tema @@ -336,9 +336,17 @@ es: attributes: payload_url: invalid: "La URL no es válida. Debería incluir http:// or https://. Y no está permitido ningún espacio en blanco." + custom_emoji: + attributes: + name: + taken: está siendo usado por otro emoji + topic_status_update: + attributes: + execute_at: + in_the_past: "debe ser en el futuro." <<: *errors user_profile: - no_info_me: "
    El campo 'Sobre mí' de tu perfil está vacío, ¿Te gustaría completarlo?
    " + no_info_me: "
    el campo \"Acerca de mí\" de tu perfil está sin rellenar, ¿te completarlo ahora?
    " no_info_other: "
    %{name} no has completado aún el campo 'Sobre mí' de tu perfil
    " vip_category_name: "Sala VIP" vip_category_description: "Una categoría exclusiva para miembros con un nivel de confianza de 3 o más." @@ -543,6 +551,12 @@ es: approval_required: "Un moderador debe aprobar manualmente tu nueva cuenta antes de que puedas acceder a este foro. ¡Recibirás un email cuando tu cuenta sea aprobada!" missing_session: "No hemos podido detectar si tu cuenta fue creada. Por favor, asegúrate de tener activadas las cookies en tu navegador." activated: "Disculpa, esta cuenta ya ha sido activada." + admin_confirm: + title: "Confirmar cuenta de administrador" + description: "¿Estás seguro de que quieres que %{target_username} sea un administrador?" + grant: "Conceder acceso administrativo" + complete: "%{target_username} es ahora un administrador." + back_to: "Volver a %{title}" post_action_types: off_topic: title: 'Off-Topic' @@ -1124,6 +1138,7 @@ es: max_age_unmatched_ips: "Eliminar entradas de IP que no coincidan después de (N) días." num_flaggers_to_close_topic: "Número mínimo de denunciantes únicos requerido para pausar un tema automáticamente para intervención" num_flags_to_close_topic: "Valor mínimo de reportes activos requerido para pausar un tema automáticamente para intervención" + num_hours_to_close_topic: "Número de horas que pausar este tema para intervenir." auto_respond_to_flag_actions: "Activar la respuesta automática al deshacer un reporte." min_first_post_typing_time: "Mínimo período de tiempo en milisegundos en el que un usuario debe estar tecleando durante su primer post, si no se llega a este umbral entrará automáticamente a la cola de moderación. Establece esta opción a 0 para desactivarla (no recomendado)." auto_block_fast_typers_on_first_post: "Bloquear automáticamente a usuarios que no cumplan el umbral establecido en min_first_post_typing_time" @@ -1333,10 +1348,13 @@ es: topic: 'Resultados' user: 'Usuarios' sso: + login_error: "Error de inicio de sesión" not_found: "No se pudo encontrar tu cuenta. Por favor contacta al administrador del sitio." account_not_approved: "Tu cuenta está pendiente de aprobación. Recibirás una notificación por email cuando se apruebe." unknown_error: "Hay un problema con tu cuenta. Por favor contacta al administrador del sitio." timeout_expired: "Se superó el tiempo para el inicio de sesión, por favor inténtalo de nuevo." + no_email: "No se ha proporcionado una dirección de email. Por favor, contacta al administrador del sitio." + email_error: "No se ha podido registrar la cuenta con la dirección de email %{email}. Por favor, contacta al administrador del sitio." original_poster: "Autor original" most_posts: "Con más posts" most_recent_poster: "Autor más reciente" @@ -1390,6 +1408,24 @@ es: autoclosed_enabled_lastpost_minutes: one: "Este tema se cerró automáticamente 1 minuto después del último post. No se permiten nuevas respuestas." other: "Este tema se cerró automáticamente %{count} minutos después del último post. No se permiten nuevas respuestas." + autoclosed_disabled_days: + one: "Este tema se abrió automáticamente después de 1 día." + other: "Este tema se abrió automáticamente después de %{count} días." + autoclosed_disabled_hours: + one: "Este tema se abrió automáticamente después de 1 hora." + other: "Este tema se abrió automáticamente después de %{count} horas." + autoclosed_disabled_minutes: + one: "Este tema se abrió automáticamente después de 1 minuto." + other: "Este tema se abrió automáticamente después de %{count} minutos." + autoclosed_disabled_lastpost_days: + one: "Este tema se abrió automáticamente 1 día después de su última respuesta." + other: "Este tema se abrió automáticamente %{count} días después de su última respuesta." + autoclosed_disabled_lastpost_hours: + one: "Este tema se abrió automáticamente 1 hora después de su última respuesta." + other: "Este tema se abrió automáticamente %{count} horas después de su última respuesta." + autoclosed_disabled_lastpost_minutes: + one: "Este tema se abrió automáticamente 1 minuto después de su última respuesta." + other: "Este tema se abrió automáticamente %{count} minutos después de su última respuesta." autoclosed_disabled: "El tema ahora está en abierto, se permiten respuestas." autoclosed_disabled_lastpost: "Este tema está ahora abierto. Se permiten nuevas respuestas." pinned_enabled: "Este tema ahora está destacado. Aparecerá en primer lugar en la lista de su categoría hasta que se deshaga el destacado de forma general por los moderadores o de forma particular por cada usuario para sí." @@ -1403,7 +1439,7 @@ es: incorrect_username_email_or_password: "Nombre de usuario, dirección de correo o contraseña incorrectos" wait_approval: "Gracias por registrarte. Te notificaremos cuando tu cuenta haya sido aprobada." active: "Tu cuenta está activa y lista para usar." - activate_email: "

    ¡Ya casi has terminado! Te enviamos un correo de activación a %{email}. Por favor sigue las instrucciones de ese correo para activar tu cuenta.

    Si no llega, mira en la carpeta de spam, o intenta iniciar sesión de nuevo para enviar de nuevo otro correo de activación.

    " + activate_email: "

    ¡Ya casi has terminado! Te hemos enviado un correo a %{email}. Por favor, sigue las instrucciones en tu email para activar tu cuenta.

    Si no llega, comprueba tu buzón de correo basura, intenta iniciar sesión de nuevo para que te enviemos otro correo de activación o para cambiar el correo de activación.

    " not_activated: "Aún no puedes iniciar sesión. Te hemos enviado un email de activación. Por favor, sigue las instrucciones en el email para activar tu cuenta." not_allowed_from_ip_address: "No puedes iniciar sesión como %{username} desde esa dirección IP." admin_not_allowed_from_ip_address: "No puedes iniciar sesión como admin desde esta dirección IP." @@ -1524,7 +1560,6 @@ es: Esta invitación viene de un usuario en el que confiamos, por lo que se te creará una cuenta automáticamente. custom_invite_forum_mailer: - title: "Invitación suscripción del foro" subject_template: "%{invitee_name} te ha invitado a %{site_domain_name}" text_body_template: | %{invitee_name} te ha invitado a @@ -1546,14 +1581,15 @@ es: title: "Instrucciones para la contraseña del invitado" subject_template: "Asigna una contraseña para tu cuenta en %{site_name}" text_body_template: | - Gracias por aceptar tu invitación a %{site_name} -- ¡te damos la bienvenida! + Gracias por aceptar la invitación a %{site_name} -- ¡de tamos la bienvenida! - Haz clic en el siguiente enlace para elegir una contraseña: - %{base_url}/users/password-reset/%{email_token} + Haz clic en el enlace para escoger una contraseña: + %{base_url}/u/password-reset/%{email_token} - (Si el enlace anterior ha caducado, utiliza "Olvidé mi contraseña" cuando vayas a iniciar sesión con tu dirección de email.) + (si el enlace de arriba ha expirado, escoge "Olvidé mi contraseña" al iniciar sesión con tu dirección de email.) download_backup_mailer: title: "Descargar Mailer Backup" + subject_template: "[%{email_prefix}] Descarga de copia de seguridad" text_body_template: | Aquí está el enlace de [descarga del backup del sitio](%{backup_file_path}) que solicitaste. @@ -1562,8 +1598,16 @@ es: (Si tú *no has* solicitado esta descarga, deberías estar seriamente preocupado -- alguien tiene acceso como administrador a tu sitio.) no_token: | Disculpa, este enlace de descarga del backup ya ha sido usado o ha expirado. + admin_confirmation_mailer: + title: "Confirmación de administrador" + subject_template: "[%{email_prefix}] Confirma una nueva cuenta de administrador" + text_body_template: | + Por favor, confirma que quieres añadir a **%{target_username}** como administrador en tu foro. + + [Confirmar cuenta de administrador](%{admin_confirm_url}) test_mailer: title: "Mail de prueba" + subject_template: "[%{email_prefix}] Prueba de entrega de correo electrónico" text_body_template: | Este es un correo electrónico de prueba de @@ -1607,6 +1651,7 @@ es: [mt]: http://www.mail-tester.com/ new_version_mailer: title: "Email de Nueva versión" + subject_template: "[%{email_prefix}] Nueva versión de Discourse, actualización disponible" text_body_template: | ¡Genial, una nueva versión de [Discourse](http://www.discourse.org) está disponible! @@ -1620,6 +1665,7 @@ es: - Visita [meta.discourse.org](https://meta.discourse.org) para noticias, debates y ayuda para Discourse. new_version_mailer_with_notes: title: "Email de Nueva versión con notas" + subject_template: "[%{email_prefix}] actualización disponible" text_body_template: |+ ¡Genial, una nueva versión de [Discourse](http://www.discourse.org) está disponible! @@ -1638,6 +1684,9 @@ es: queued_posts_reminder: title: "Recordatorio de post en revisión" + subject_template: + one: "[%{email_prefix}] 1 tema esperando ser revisados" + other: "[%{email_prefix}] %{count} temas esperando ser revisados" text_body_template: | Hola, @@ -1653,7 +1702,9 @@ es: disagreed: "Gracias por hacérnoslo saber. Estamos revisándolo." deferred: "Gracias por hacérnoslo saber. Estamos revisándolo." deferred_and_deleted: "Gracias por hacérnoslo saber. Hemos eliminado el post." - temporarily_closed_due_to_flags: "Este tema está temporalmente cerrado debido a un gran número de reportes de la comunidad." + temporarily_closed_due_to_flags: + one: "Este tema ha sido cerrado automáticamente durante 1 hora debido a un gran número de reportes de la comunidad." + other: "Este tema ha sido cerrado automáticamente durante %{count} horas debido a un gran número de reportes de la comunidad." system_messages: post_hidden: title: "Post oculto" @@ -1672,11 +1723,6 @@ es: Sin embargo, si el mensaje es ocultado por la comunidad una segunda vez, seguirá ocultado hasta que el staff lo revise – y se podrían tomar medidas incluida la posible suspensión de tu cuenta. Para más información sobre lo que consideramos adecuado, lee las [directrices de la comunidad](%{base_url}/guidelines). - usage_tips: - text_body_template: | - Si quieres unos consejos para empezar, [echa un vistazo a esta entrada de blog](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). - - Conforme vayas participando por aquí, te iremos conociendo más, y tus limitaciones temporales como nuevo usuario serán levantadas. Con el tiempo ganarás [niveles de confianza](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) que incluyen habilidades especiales para ayudarnos a gestionar la comunidad juntos. welcome_user: title: "Bienvenido Usuario" subject_template: "¡Bienvenido a %{site_name}!" @@ -1792,48 +1838,56 @@ es: text_body_template: "Lo sentimos, pero la exportación de datos falló. Por favor, verifica los registros o contacta a un miembro del staff." email_reject_insufficient_trust_level: title: "Email rechazado, insuficiente nivel de confianza" + subject_template: "[%{email_prefix}] Problema de correo --Nivel de confianza insuficiente" text_body_template: | Lo sentimos, pero tu mensaje de email a %{destination} (con título %{former_title}) no ha funcionado. Tu cuenta no tiene el nivel de confianza necesario para publicar nuevos temas a esta dirección de email. Si crees que se trata de un error, contacta a un miembro del staff. email_reject_user_not_found: title: "Email rechazado, cuenta de usuario no encontrada" + subject_template: "[%{email_prefix}] Problema de correo -- Usuario no encontrado" text_body_template: | Lo sentimos, pero tu mensaje de email para %{destination} (titulado %{former_title}) no se ha enviado. No hay una cuenta asociada con esta dirección de email. Intenta enviarlo con otra dirección de email o contacta con algún miembro del Staff. email_reject_screened_email: title: "Email rechazado, correo bloqueado" + subject_template: "[%{email_prefix}] Poblema de correo -- Email bloqueado" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) se ha rechazado. No hay una cuenta asociada con esta dirección de email. Intenta enviarlo con otra dirección de email o contacta con algún miembro del Staff. email_reject_inactive_user: title: "Email rechazado, usuario inactivo" + subject_template: "[%{email_prefix}] Problema de correo -- Usuario inactivo" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no se entregó. La cuenta asociada a esta dirección de email no ha sido activada. Por favor activa tu cuenta antes de enviar emails. email_reject_blocked_user: title: "Email rechazado, usuario bloqueado" + subject_template: "[%{email_prefix}] Problema de correo -- Usuario bloqueado" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no se entregó. La cuenta asociada a esta dirección de email ha sido bloqueada. email_reject_reply_user_not_matching: title: "Email rechazado, el usuario no coincide" + subject_template: "[%{email_prefix}] Problema de correo -- Dirección de respuesta inesperada" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no se ha enviado. No hay una cuenta asociada con esta dirección de email. Intenta enviarlo con otra dirección de email o contacta con algún miembro del Staff. email_reject_no_account: title: "Email rechazado, no hay cuenta" + subject_template: "[%{email_prefix}] Problema de correo -- Cuenta desconocida" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) se ha rechazado. No hay una cuenta de usuario asociada con esta dirección de email. Intenta enviarlo con otra dirección de email o contacta con algún miembro del Staff. email_reject_empty: title: "Email rechazado, email vacío" + subject_template: "[%{email_prefix}] Problema de correo -- Sin contenido" text_body_template: | Lo sentimos, pero tu email para %{destination} (titled %{former_title}) no funcionó. @@ -1842,18 +1896,21 @@ es: Si estás teniendo este problema y _sí_ incluiste contenido, inténtalo de nuevo con un formato más simple. email_reject_parsing: title: "Email rechazado, contenido erróneo" + subject_template: "[%{email_prefix}] Problema de correo -- Contenido no reconocido" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. No hemos encontrado tu respuesta en el email. **Asegúrate de escribir tu entera respuesta al principio del email** - no podemos analizar respuestas entre líneas. email_reject_invalid_access: title: "Email rechazado, acceso inválido" + subject_template: "[%{email_prefix}] Problema de correo -- Acceso inválido" text_body_template: | Lo sentimos, pero tu mensaje por email a %{destination} (titled %{former_title}) no funcionó. Tu cuenta no tiene los privilegios para publicar nuevos temas en esa categoría. Si crees que esto es un error, contacta a un moderador o administrador. email_reject_strangers_not_allowed: title: "Rechazar emails desconocidos No permitido" + subject_template: "[%{email_prefix}] Problema de correo -- Acceso inválido" text_body_template: |+ Lo sentimos, pero tu mensaje de email para %{destination} (titulado %{former_title}) no fue aceptado. @@ -1862,12 +1919,14 @@ es: email_reject_invalid_post: title: "Email rechazado, post inválido" + subject_template: "[%{email_prefix}] Problema de correo -- Error al publicar" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) se ha rechazado. Posibles causas: formato complejo, mensaje demasiado extenso o demasiado breve. Por favor, inténtalo de nuevo, o bien publícalo en el foro. email_reject_invalid_post_specified: title: "Email Rechazado, Post Especificado Inválido" + subject_template: "[%{email_prefix}] Problema de correo -- Error al publicar" text_body_template: | Lo sentimos, pero tu mensaje de email para %{destination} (titled %{former_title}) no funcionó. @@ -1878,42 +1937,49 @@ es: Si puedes corregir el problema sugerido, por favor inténtalo de nuevo. email_reject_invalid_post_action: title: "Email Rechazado, Acción de Post Inválida" + subject_template: "[%{email_prefix}] Problema de correo -- Acción inválida" text_body_template: | Lo sentimos, pero tu mensaje de email para %{destination} (titled %{former_title}) no funcionó. La "Post Action" no fue reconocida. Por favor prueba luego o envía el mensaje desde el sitio web de forma tradicional si el error continua. email_reject_reply_key: title: "El email rechazó la clave de respuesta" + subject_template: "[%{email_prefix}] Problema de correo -- Clave de respuesta desconocida" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) se ha rechazado. La clave de respuesta proporcionada no es válida o es desconocida, por lo que no sabemos a qué responde este email. Contacta con algún miembro del staff. email_reject_bad_destination_address: title: "Email rechazado, mala dirección del destinatario" + subject_template: "[%{email_prefix}] Problema de correo -- Destinatario desconocido" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. Ninguna de las direcciones de emails ha sido reconocida. Por favor asegúrate de que estás enviando los correos a las direcciones correctas provistas por los administradores. email_reject_topic_not_found: title: "Email rechazado, tema no encontrado" + subject_template: "[%{email_prefix}] Problema de correo -- Tema no encontrado" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. El tema al que estás respondiendo ya no existe, quizá debe haber sido eliminado? Si crees que esto es un error, contacta a un administrador. email_reject_topic_closed: title: "Email rechazado, tema cerrado" + subject_template: "[%{email_prefix}] Problema de correo -- Tema cerrado" text_body_template: | Lo sentimos, pero tu email para %{destination} (titulado %{former_title}) no funcionó. El tema actualmente está cerrado y no acepta más respuestas. Si crees que esto es un error, contacta a un administrador. email_reject_auto_generated: title: "Email autogenerado rechazado" + subject_template: "[%{email_prefix}] Problema de correo -- Respuesta generada automáticamente" text_body_template: | Lo sentimos, pero tu mensaje de correo electrónico para %{destination} (titulado %{former_title}) no funcionó. Tu respuesta de correo fue marcada como "generada automáticamente", lo que significa que fue creado automáticamente por una computadora, en vez de un ser humano; no aceptamos este tipo de emails. Si crees que se trata de un error contacta con un administrador. email_error_notification: title: "Email, error de notificación" + subject_template: "[%{email_prefix}] Problema de correo -- Error de autenticación de POP" text_body_template: |+ Hubo un error de autenticación mientras se ejecutaba el polling de los emails desde el servidor POP. @@ -2006,6 +2072,17 @@ es: Algunos problemas han sido reportados en el panel de administración. [Por favor revisar y corregir](%{base_url}/admin). + new_user_of_the_month: + title: "¡Eres un usuario nuevo del mes!" + subject_template: "¡Eres un usuario nuevo del mes!" + text_body_template: | + Enhorabuena, has ganado el distintivo **Premio al nuevo usuario del mes de %{month_year}**. :trophy: + + Te has convertido rápidamente en un valioso miembro de nuestra comunidad y estamos disfrutando con tus posts. + + Este premio será visible para siempre como un distintivo en tu [página de usuario](/my/badges). + + Gracias, ¡y sigue así! unsubscribe_link: | Para darte de baja de estos emails, [haz clic aquí](%{unsubscribe_url}). unsubscribe_link_and_mail: | @@ -2053,6 +2130,7 @@ es: > %{site_title} -- %{site_description} user_invited_to_private_message_pm: title: "Usuario invitado a MP" + subject_template: "[%{email_prefix}] %{username} te ha invitado a un mensaje '%{topic_title}'" text_body_template: | %{header_instructions} @@ -2061,6 +2139,7 @@ es: %{respond_instructions} user_invited_to_private_message_pm_staged: title: "Usuario invitado a MP provisional" + subject_template: "[%{email_prefix}] %{username} te ha invitado a un mensaje '%{topic_title}'" text_body_template: | %{header_instructions} @@ -2069,6 +2148,7 @@ es: %{respond_instructions} user_invited_to_topic: title: "Usuario invitado a tema" + subject_template: "[%{email_prefix}] %{username} te ha invitado a un tema '%{topic_title}'" text_body_template: | %{header_instructions} @@ -2077,6 +2157,7 @@ es: %{respond_instructions} user_replied: title: "Usuario respondió" + subject_template: "[%{email_prefix}] %{topic_title}" text_body_template: | %{header_instructions} @@ -2087,6 +2168,7 @@ es: %{respond_instructions} user_replied_pm: title: "Usuario respondió por MP" + subject_template: "[%{email_prefix}] [MP] %{topic_title}" text_body_template: | %{header_instructions} @@ -2097,6 +2179,7 @@ es: %{respond_instructions} user_quoted: title: "Usuario citado" + subject_template: "[%{email_prefix}] %{topic_title}" text_body_template: | %{header_instructions} @@ -2107,6 +2190,7 @@ es: %{respond_instructions} user_linked: title: "Usuario enlazado" + subject_template: "[%{email_prefix}] %{topic_title}" text_body_template: | %{header_instructions} @@ -2117,6 +2201,7 @@ es: %{respond_instructions} user_mentioned: title: "Usuario mencionado" + subject_template: "[%{email_prefix}] %{topic_title}" text_body_template: | %{header_instructions} @@ -2127,6 +2212,7 @@ es: %{respond_instructions} user_group_mentioned: title: "Grupo del usuario mencionado" + subject_template: "[%{email_prefix}] %{topic_title}" text_body_template: | %{header_instructions} @@ -2137,6 +2223,7 @@ es: %{respond_instructions} user_posted: title: "Usuario publicó" + subject_template: "[%{email_prefix}] %{topic_title}" text_body_template: | %{header_instructions} @@ -2147,6 +2234,7 @@ es: %{respond_instructions} user_watching_first_post: title: "Usuarios vigilando el primer Post" + subject_template: "[%{email_prefix}] %{topic_title}" text_body_template: | %{header_instructions} @@ -2157,6 +2245,7 @@ es: %{respond_instructions} user_posted_pm: title: "Usuario envió un MP" + subject_template: "[%{email_prefix}] [MP] %{topic_title}" text_body_template: | %{header_instructions} @@ -2185,12 +2274,14 @@ es: join_the_discussion: "Leer más" popular_posts: "Temas populares" more_new: "Nuevo para ti" + subject_template: "[%{email_prefix}] Resumen" unsubscribe: "Este resumen se envía desde %{site_link} cuando pasa un tiempo desde tu última visita. Para cancelar tu suscripción %{unsubscribe_link}." click_here: "clic aquí" from: "resumen de %{site_name}" preheader: "Un corto resumen desde tu última visita el %{last_seen_at}" mailing_list: why: "Actividad en %{site_link} a %{date}" + subject_template: "[%{email_prefix}] Resumen del %{date}" unsubscribe: "Este resumen se envía diariamente dado que el modo lista de correo está activado. Puedes darte de baja %{unsubscribe_link}." from: "%{site_name} resumen" new_topics: "Nuevos temas" @@ -2199,54 +2290,65 @@ es: back_to_top: "Volver al inicio" forgot_password: title: "Olvidé la Contraseña" + subject_template: "[%{email_prefix}] Reestablecimiento de contraseña" text_body_template: | - Alguien ha pedido restablecer tu contraseña en [%{site_name}](%{base_url}). + Alguien ha pedido reestablecer tu contraseña en [%{site_name}](%{base_url}). - Si no fuiste tú, puedes ignorar este email de forma segura. + Si no has sido tú, puedes ignorar este email sin problema. - Haz clic en el siguiente enlace para cambiar tu contraseña: - %{base_url}/users/password-reset/%{email_token} + Haz clic en el siguiente enlace para escoger una nueva contraseña: + %{base_url}/u/password-reset/%{email_token} set_password: title: "Pon una contraseña" + subject_template: "[%{email_prefix}] Establecer contraseña" text_body_template: | - Se ha solicitado añadir una contraseña a tu cuenta en [%{site_name}](%{base_url}). Otra opción es iniciar sesión usando cualquier servicio online soportado (Google, Twitter, etc) que esté asociado con esta dirección de email validada. + Alguien ha pedido añadir una contraseña a tu cuenta en [%{site_name}](%{base_url}). + Alternativamente, puedes iniciar sesión con cualquier servicio disponible (Google, Facebook, etc.) que esté asociado con tu dirección de email. - Si no formulaste esta petición, por favor, ignora este correo. + Si no has hecho esta solicitud, puedes ignorar este email sin problema. - Haz clic en el siguiente enlace para escoger una contraseña: - %{base_url}/users/password-reset/%{email_token} + Haz clic en el siguiente enlace para establecer una contraseña: + %{base_url}/u/password-reset/%{email_token} admin_login: title: "Admin Login" + subject_template: "[%{email_prefix}] Inicio de sesión" text_body_template: | - Alguien pidió iniciar sesión con tu cuenta en [%{site_name}](%{base_url}). + Alguien ha pedido iniciar sesión con tu cuenta en [%{site_name}](%{base_url}). - Si tú no realizaste esta petición, puedes ignorar este email. + Si no lo has solicitado tú, puedes ignorar este email sin problema. Haz clic en el siguiente enlace para iniciar sesión: - %{base_url}/users/admin-login/%{email_token} + %{base_url}/u/admin-login/%{email_token} account_created: title: "Cuenta creada" - text_body_template: | - Una nueva cuenta ha sido creada por ti en %{site_name} + subject_template: "[%{email_prefix}] Tu nueva cuenta" + text_body_template: |+ + Se ha creado una nueva cuenta para ti en %{site_name} + + Haz clic en el siguiente enlace para escoger una contraseña para tu nueva cuenta: + %{base_url}/u/password-reset/%{email_token} - Pulsa en el siguiente enlace para escoger una contraseña para tu nueva cuenta: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "Confirmar Nuevo Email" + subject_template: "[%{email_prefix}] Confirma tu dirección de email" text_body_template: | - Confirma tu nueva dirección de email para %{site_name} haciendo clic en el siguiente enlace: + Confirma tu dirección de email para %{site_name} haciendo clic en el siguiente enlace: - %{base_url}/users/authorize-email/%{email_token} + %{base_url}/u/authorize-email/%{email_token} confirm_old_email: title: "Confirmar Viejo Email" + subject_template: "[%{email_prefix}] Confirma tu dirección de email actual" text_body_template: | - Antes de cambiar tu dirección de correo electrónico, necesitamos que confirmes que tienes acceso a dicho email. Después de completar este paso, podrás confirmar la nueva dirección de correo electrónico. + Antes de cambiar tu dirección de email, necesitamos confirmar que controlas + la dirección de email actual. Cuando termines este paso, confirmaremos + la nueva dirección de email. - Confirma tu actual correo electrónico para %{site_name} haciendo clic en el siguiente enlace: + Confirma tu dirección de email actual en %{site_name} haciendo clic en el siguiente enlace: - %{base_url}/users/authorize-email/%{email_token} + %{base_url}/u/authorize-email/%{email_token} notify_old_email: title: "Antiguo email de notificaciones" + subject_template: "[%{email_prefix}] Tu dirección de correo ha sido cambiada" text_body_template: | Este es un mensaje automático que se ha generado para comunicarte que tu email para el sitio %{site_name} ha sido modificado. Si ha habido un error, por favor contacta al administrador. @@ -2259,29 +2361,30 @@ es: text_body_template: | ¡Te damos la bienvenida a %{site_name}! - Un miembro del staff aprobó tu cuenta en %{site_name}. + Un miembro del staff ha aprobado tu cuenta de %{site_name}. Haz clic en el siguiente enlace para confirmar y activar tu nueva cuenta: - %{base_url}/users/activate-account/%{email_token} + %{base_url}/u/activate-account/%{email_token} - Si no puedes hacer clic en el enlace, intenta copiándolo y pegándolo en la barra de direcciones de tu navegador. + Si el enlace de arriba no funciona, prueba a copiarlo y pegarlo en la barra de direcciones de tu navegador de internet. %{new_user_tips} - Creemos en una comunidad con un [comportamiento civilizado](%{base_url}/faq). + Creemos en una [comunidad con comportamiento civilizado](%{base_url}/guidelines) en todo momento. ¡Disfruta de tu estancia! - (Si necesitas comunicarte por privado con [algún miembro del staff](%{base_url}/about) como nuevo usuario, puedes responder a este mensaje.) + (Si necesitas comunicarte con algún [miembro del staff](%{base_url}/about) como nuevo usuario, puedes responder a este mensaje.) signup: title: "Registrate" + subject_template: "[%{email_prefix}] Confirma tu nueva cuenta" text_body_template: | - ¡Bienvenido a %{site_name}! + ¡Te damos la bienvenida a %{site_name}! - Haz clic en el siguiente enlace para confirmar y activar tu nueva cuenta - %{base_url}/users/activate-account/%{email_token} + Haz clic en el siguiente enlace para confirmar y activar tu nueva cuenta: + %{base_url}/u/activate-account/%{email_token} - Si el enlace anterior no es clicable, prueba copiándolo y pegándolo en la barra de direcciones de tu navegador. + Si el enlace de arriba no funciona, prueba a copiarlo y pegarlo a la barra de direcciones de tu navegador de internet. page_not_found: title: "¡Ups! Esa página no existe o es privada." popular_topics: "Populares" @@ -2784,6 +2887,12 @@ es: description: Respondió a un post vía email long_description: | Este distintivo se concede la primera vez que respondes a un post por email :e-mail:. + new_user_of_the_month: + name: "Usuario nuevo del mes" + description: Contribuciones excepcionales en su primer mes + long_description: | + Este distintivo se entrega para felicitar a dos nuevos usuarios cada mes por sus excelentes contribuciones, medido por la frecuencia en la que sus posts reciben "Me gusta" y de parte de quién. + badge_title_metadata: "%{display_name} distintivo en %{site_title}" admin_login: success: "Email enviado" error: "¡Error!" @@ -2815,7 +2924,6 @@ es: safe_mode: title: "Activar modo seguro" description: "El modo seguro permite probar tu página sin cargar plugins o customizaciones." - no_customizations: "Desactivar todas las customizaciones" only_official: "Desactivar plugins no oficiale" no_plugins: "Desactivar todos los plugins" enter: "Activar modo seguro" diff --git a/config/locales/server.et.yml b/config/locales/server.et.yml index 48da7be45f..c54f8cc7df 100644 --- a/config/locales/server.et.yml +++ b/config/locales/server.et.yml @@ -27,7 +27,6 @@ et: topics: "Teemad" posts: "postitused" loading: "Laetakse" - powered_by_html: 'Ajamiks on Discourse... vaadata JavaScript lubatuna' log_in: "Logi sisse" purge_reason: "Kustutatud automaatselt kui mahajäetud, deaktiveeritud konto" disable_remote_images_download_reason: "Väliste piltide allalaadimine keelati, kuna polnud polnud piisavalt vaba ruumi." @@ -276,16 +275,12 @@ et: attributes: hex: invalid: "pole korrektne värv" - post_reply: - base: - different_topic: "Postitus ja vastus peavad kuuluma samasse teemasse." web_hook: attributes: payload_url: invalid: "URL on vigane. URL peab sisaldama http:// või https://. Ning tühikud ei ole lubatud." <<: *errors user_profile: - no_info_me: "
    Sinu profiili väli 'Minust' on hetkel tühi, kas sooviksid selle ära täita?
    " no_info_other: "
    %{name} profiilis on väli 'Minust' veel täitmata
    " vip_category_name: "Lounge" vip_category_description: "Liik, mis on eksklusiivne kasutajatele, kellel on usaldustase 3 või kõrgem." @@ -897,10 +892,8 @@ et: description: Kasutasid postituses emotikoni first_mention: name: Esimene mainimine - description: Mainisid postituses kasutajat first_reply_by_email: name: Esimene vastamine meiliga - description: Vastasid postitusele läbi e-maili admin_login: success: "Kiri saadetud" error: "Viga!" @@ -921,7 +914,6 @@ et: title: "Saada aktiveerimismeil uuesti" safe_mode: title: "Sisene turvalisse režiimi" - no_customizations: "Keela kõik saidi kohandused" only_official: "Keela kõik mitteametlikud pluginad" no_plugins: "Keela kõik pluginad" enter: "Sisene turvalisse režiimi" diff --git a/config/locales/server.fa_IR.yml b/config/locales/server.fa_IR.yml index 6e0b6510ee..610ac8d0d6 100644 --- a/config/locales/server.fa_IR.yml +++ b/config/locales/server.fa_IR.yml @@ -27,7 +27,6 @@ fa_IR: topics: "موضوعات" posts: "نوشته ها" loading: "بارگذاری..." - powered_by_html: 'قدرت گرفته ازدیسکورسفارسی سازی دیسکورس فارسی, برای نمایش بهتر جاوا اسکریپت را فعال کنید.' log_in: "ورود" purge_reason: "حساب کاربری غیرفعال. حذف شده به صورت خودکار به علت عدم استفاده." disable_remote_images_download_reason: "عکس های ریموت دانلود شده غیرفعال شدند زیرا آنجا فضای کافی در دیسک وجود نداشت" @@ -207,6 +206,8 @@ fa_IR: attributes: category: name: "نام دسته" + topic: + title: 'عنوان' post: raw: "متن" user_profile: @@ -234,7 +235,6 @@ fa_IR: invalid: "این یک رنگ معتبر نیست" <<: *errors user_profile: - no_info_me: "
    درباره من نمایه شما خالی در حال حاضر خالی است٬ علاقه دارید آن را تکمیل کنید ؟
    " no_info_other: "
    %{name}هنوز چیزی وارد نکرده درباره خودش در نمایه شان. " vip_category_name: "زبان" vip_category_description: "دسته مخصوص برای اعضا با سطح اعتماد 3 و بالاتر." @@ -930,7 +930,6 @@ fa_IR: incorrect_username_email_or_password: "نام کاربری، ایمیل یا رمز عبور نادرست" wait_approval: "ممنون برای ثبت نام. ما به شما اطلاع می دهیم وقتی حساب کاربری شما تایید شد." active: "حساب کاربریتان فعال شده و آماده‌ی استفاده است." - activate_email: "

    تقریبا ثبت نام انجام شد! ما ایمیل فعال سازی را برای ما ارسال کردیم %{email}. لطفا به ایمیل خود برای فعال سازی حساب کاربر خود مراجعه نمایید.

    اگر ایمل فعال سازی نبود، لطفا پوشه اسپم ایمیل خود را برسی نمایید،اگر ایمیل ارسال نشده است با ورود به سایت دوباره ایمیل فعال سازی ارسال نمایید.

    " not_activated: "هنوز نمی توانید وارد سیستم شوید. ما ایمیل برای فعال سازی برای شما ارسال کردیم. لطفا دستور عمل را در ایمیل برای فعال سازی حساب کاربریتان دنبال کنید." not_allowed_from_ip_address: "شما نمی‌توانید با این آی‌پی آدرس وارد حساب کاربری {username}% شوید." admin_not_allowed_from_ip_address: "شما نمی تواند با این اپی آدرس به عنوان مدیر وارد سیستم شوید." @@ -980,7 +979,6 @@ fa_IR: disagreed: "ممنون از اینکه به ما اطلاع دادید. ما بهش نگاه می کنیم" deferred: "ممنون از اینکه به ما اطلاع دادید. ما بهش نگاه می کنیم" deferred_and_deleted: "ممنون از اینکه به ما اطلاع دادید. ما نوشته را پاک می کنیم." - temporarily_closed_due_to_flags: "این عنوان به صورت موقت به خاطر تعداد فراوان گزارش‌های رسیده از کاربران بسته شده است." system_messages: welcome_user: subject_template: "به %{site_name} خوش آمدید!" @@ -1050,73 +1048,8 @@ fa_IR: digest: why: "یک گزارش کوتاه از %{site_link} از آخرین باری که بازدید کردید در %{last_seen_at}" click_here: "اینجا کلیک کنید" - forgot_password: - text_body_template: | - شخصی برای ریست رمز عبور سوال کرد[%{site_name}](%{base_url}). - - - اگر شما نبود٬ می توانید با خیال راحت این ایمیل را نادیده بگیرید. - - - بر روی پیوند پیش رو برای انتخاب رمز عبور جدید کلیک کنید. - - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - شخصی برای اضافه کردن رمز عبور به حساب کاربری شما پرسیده در [%{site_name}](%{base_url}). در روش دیگر٬‌ شما می توانید با وارد شوید از طریق سرویس دیگر مثل (گوگل٬‌ فیس بوک٬‌غیره) که ایمیل برابر استفاده می کنند. - - - اگر شما نبود٬ می توانید با خیال راحت این ایمیل را نادیده بگیرید. - - بر روی پیوند پیش رو برای انتخاب رمز عبور جدید کلیک کنید. - - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - شخصی برای ورود به سیستم با حساب کاربری شما سوال کرد [%{site_name}](%{base_url}). - - اگر شما این درخواست را نداده اید می توانید این ایمیل را نادیده بگیرید. - - برای ورود به سیستم لینک پیش رو را کلیک کنید. : - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - حساب کاربری جدید برای شما ساخته شد در %{site_name} - - بر روری پیوند پیش رو کلیک کنید برای انتخاب رمز برای حساب کاربری جدیدتان: - - %{base_url}/users/password-reset/%{email_token} signup_after_approval: subject_template: "شما تایید شدید %{site_name}! " - text_body_template: | - خوش آمدید به %{site_name}! - - یکی از مدیران حساب کاربری شما را تایید کرد در %{site_name}. - - بر روی پیوند پیش رو کلیک کنید تا حساب کاربری شما تایید شود: - %{base_url}/users/activate-account/%{email_token} - - اگر پیوند بالا قابل کلیک کردن نیست٬ پیوند را کپی کنید و در مرور گر وب انتقال دهید. - - %{new_user_tips} - - - ما به [رفتار انجمن متمدن] معتقدیم (%{base_url}/guidelines) برای همیشه. - - از بودنتان در اینجا لذت ببرید! - - ( اگر شما نیاز به ارتباط با مدیران را دارید[staff members](%{base_url}/about) به عنوان یک کاربر جدید٬ لطفا به این پیام پاسخ دهید.) - signup: - text_body_template: | - خوش آمدید به %{site_name}! - - - برای فعال نمودن حساب کاربریتان بر روی پیوند پیش رو کلیک کنید: - - %{base_url}/users/activate-account/%{email_token} - - - اگر پیوند بالا قابل کلیک کردن نیست٬ پیوند را کپی کنید و در مرور گر وب انتقال دهید. page_not_found: popular_topics: "محبوب" recent_topics: "اخیر" diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index efb214f84a..411d7d6e32 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -27,12 +27,15 @@ fi: topics: "Ketjut" posts: "viestit" loading: "Lataa" - powered_by_html: 'Voimanlähteenä Discourse, toimii parhaiten, kun JavaScript on käytössä' + powered_by_html: 'Voimanlähteenä Discourse, toimii parhaiten, kun JavaScript on käytössä' log_in: "Kirjaudu" purge_reason: "Hylätty, aktivoimaton tili poistettiin automaattisesti" disable_remote_images_download_reason: "Linkattujen kuvien lataaminen poistettiin käytöstä vähäisen tallennustilan vuoksi." anonymous: "Anonyymejä" remove_posts_deleted_by_author: "Kirjoittajan poistama" + themes: + bad_color_scheme: "Teemaa ei voitu päivittää, värimalli ei kelpaa" + other_error: "Jotakin meni vikaan, kun teemaa päivitettiin" emails: incoming: default_subject: "Tämä ketju tarvitsee otsikon" @@ -826,7 +829,7 @@ fi: poll_pop3_auth_error: "Yhteys POP3-palvelimelle epäonnistuu autentikaatiovirheen vuoksi. Tarkista POP3-asetukset." site_settings: censored_words: "Sanat, jotka korvataan automaattisesti merkeillä ■■■■" - censored_pattern: "Merkkijono, joka korvataan automaattisesti ■■■■" + censored_pattern: "Säännöllinen lauseke, joka korvataan automaattisesti ■■■■" delete_old_hidden_posts: "Poista automaattisesti kaikki yli 30 päivää piilotettuna olleet viestit." default_locale: "Sivuston oletuskieli (ISO 639-1 koodi)" allow_user_locale: "Salli käyttäjien vaihtaa käyttöliittymän kieli omista asetuksista" @@ -1048,7 +1051,7 @@ fi: external_system_avatars_url: "Ulkoisen avatarpalvelun URL. Sallitut vaihdokset ovat {username} {first_letter} {color} {size}" default_opengraph_image_url: "Oletuksena käytettävän opengraph-kuvan URL." twitter_summary_large_image_url: "Oletuksena käytettävän Twitter-tiivistelmäkortin kuva (tulisi olla leveydeltään ainakin 280 px leveä ja 150 px korkea)." - allow_all_attachments_for_group_messages: "Sali kaikki sähköpostiliitteet ryhmäviestessä." + allow_all_attachments_for_group_messages: "Salli kaikki sähköpostiliitteet ryhmäviesteissä." convert_pasted_images_to_hq_jpg: "Muuta liitetyt kuvat korkealaatuisiksi JPG-tiedostoiksi" convert_pasted_images_quality: "Muunnetun JPG-tiedoston laatu (1 on huonoin laatu, 100 on paras laatu)." enable_flash_video_onebox: "Ota käyttöön swf- ja flv-linkkien (Adobe Flash) onebox-tuki. VAROITUS: saattaa lisätä tietoturvariskejä." @@ -1253,14 +1256,13 @@ fi: embed_username_required: "Käyttäjänimi vaaditaan ketjun luomiseksi." embed_whitelist_selector: "CSS valitsin elementeille, jotka sallitaan upotetuissa viesteissä." embed_blacklist_selector: "CSS valitstin elementeille, jotka poistetaan upotetuista viesteistä." - notify_about_flags_after: "Jos liputuksia ei ole käsitelty näin moneen tuntiin, lähetä sähköposti contact_email osoitteeseen. Aseta 0 ottaaksesi pois käytöstä." show_create_topics_notice: "Jos palstalla on vähemmän kuin 5 julkista ketjua, huomauta ylläpitäjiä ketjujen luonnista." delete_drafts_older_than_n_days: Poista yli (n) päivää vanhat luonnokset. bootstrap_mode_min_users: "Vähimmäismäärä käyttäjiä, joka vaaditaan aloitustilan poistamiseen (aseta 0 poistaaksesi käytöstä)" prevent_anons_from_downloading_files: "Estä kirjautumattomia käyttäjiä lataamasta liitetiedostoja. VAROITUS: tämä estää viestin liitettyjen muiden tiedostojen, kuin kuvien käyttämisen sivustolla." slug_generation_method: "Valitse polkulyhenteen luomisen metodi. 'encoded' käyttää prosenttikoodausta, 'none' poistaa polkulyhenteet käytöstä." enable_emoji: "Ota emoji käyttöön" - emoji_set: "Minkälaisa emojia haluaisit käyttää?" + emoji_set: "Millaiset emojit haluat?" enforce_square_emoji: "Pakota neliö kuvasuhteeksi kaikille emojille." approve_post_count: "Viestien lukumäärä, joka tarkastetaan uusilta käyttäjiltä ja haastajilta." approve_unless_trust_level: "Tätä luottamustasoa alhaisempien käyttäjien viestit tarkastetaan" @@ -1713,9 +1715,9 @@ fi: Saadaksesi lisätietoja, lue [yhteisön säännöt](%{base_url}/guidelines). usage_tips: text_body_template: | - [Tässä blogikirjoituksessa](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/) on joitakin käteviä vinkkejä uudelle käyttäjälle. + [Tässä blogikirjoituksessa](https://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/) on joitakin käteviä vinkkejä uudelle käyttäjälle. - Sitä mukaa kun toimit täällä, opimme tuntemaan sinut ja väliaikaisia uuden käyttäjän rajoitteita poistetaan automaattisesti. Ajan myötä nouset ylemmille [luottamustasoille](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) ja saat valtuuksia joiden avulla voit osallistua yhteisömme ylläpitoon. + Sitä mukaa kun toimit täällä, opimme tuntemaan sinut ja väliaikaisia uuden käyttäjän rajoitteita poistetaan automaattisesti. Ajan myötä nouset ylemmille [luottamustasoille](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) ja saat valtuuksia, joiden avulla voit osallistua yhteisömme ylläpitoon. welcome_user: title: "Tervetuloa käyttäjä" subject_template: "Tervetuloa sivustolle %{site_name}!" @@ -2421,6 +2423,8 @@ fi: no_echo_mailing_list_mode: "Postitustilassa ei sähköposti-ilmoituksia käyttäjän omista viesteistä" color_schemes: base_theme_name: "Pohja" + default: "Vaalea värimalli" + dark: "Tumma värimalli" about: "Tietoja" guidelines: "Ohjeet" privacy: "Yksityisyys" @@ -2869,7 +2873,7 @@ fi: safe_mode: title: "Siirry vikasietotilaan" description: "Vikasietotilassa voit testata sivustoasi ilman lisäosia ja sivuston mukautuksia." - no_customizations: "Poista käytöstä sivuston mukautukset" + no_customizations: "Poista nykyinen teema käytöstä" only_official: "Poista käytöstä epäviralliset lisäosat" no_plugins: "Poista käytöstä kaikki lisäosat" enter: "Siirry vikasietotilaan" diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index 5ae81ab628..5416153d2c 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -27,12 +27,15 @@ fr: topics: "Sujets" posts: "messages" loading: "Chargement" - powered_by_html: 'Propulsé par Discourse, le rendu est meilleur avec le JavaScript activé' + powered_by_html: 'Motorisé par Discourse, nécessite l''activation de JavaScript ' log_in: "Se connecter" purge_reason: "Supprimé automatiquement comme compte abandonné et non activé" disable_remote_images_download_reason: "Le téléchargement des images externes a été désactivé faute de place suffisante sur le disque." anonymous: "Anonyme" remove_posts_deleted_by_author: "Supprimé par l'auteur" + themes: + bad_color_scheme: "Impossible de mettre à jour le thème, la palette de couleur est invalide" + other_error: "Il y a eu un problème pendant la mise à jour du thème" emails: incoming: default_subject: "Ce sujet a besoin d'un titre" @@ -110,6 +113,7 @@ fr: s3_upload_bucket_is_required: "Vous ne pouvez pas activer l'upload sur S3 avant d'avoir renseigné le 's3_upload_bucket'." invite: not_found: "Votre jeton d'invitation est invalide. Veuillez contacter l'administrateur du site." + user_exists: "Il n'y a pas besoin d'inviter %{email} qui a déjà un compte !" bulk_invite: file_should_be_csv: "Le fichier envoyé doit être au format CSV." error: "Il y a eu une erreur en envoyant ce fichier. Merci de réessayer plus tard." @@ -190,6 +194,12 @@ fr: latest: "Sujets récents" hot: "Sujets populaires" top: "Meilleurs sujets" + top_all: "Meilleurs sujets de tous les temps" + top_yearly: "Meilleurs sujets de l'année" + top_quarterly: "Meilleurs sujets du semestre" + top_monthly: "Meilleurs sujets du mois" + top_weekly: "Meilleurs sujets de la semaine" + top_daily: "Meilleurs sujets du jour" posts: "Messages récents" private_posts: "Derniers messages privés" group_posts: "Derniers messages de %{group_name}" @@ -244,6 +254,14 @@ fr: - Les critiques constructives sont les bienvenues, mais critiquez les *idées*, pas les gens. Pour plus d'informations, [consulter la charte de la communauté](/guidelines). Cet encart apparaîtra pour votre premier %{education_posts_text}. + avatar: | + ### Avez-vous pensé à une photo de profil ? + + Vous avez posté quelques sujets et réponses, mais votre photo de profil n'est pas unique comme vous -- c'est juste une lettre. + + Avez-vous pensé à **[visiter votre profil utilisateur](%{profile_path})** et ajouter une photo qui vous représente ? + + C'est plus facile de suivre les discussions et de rencontrer des personnes intéressantes dans les conversations lorsque tout le monde a une photo de profil unique ! activerecord: attributes: category: @@ -288,9 +306,17 @@ fr: attributes: payload_url: invalid: "L'URL est invalide. Elle devrait inclure http:// ou https:// et aucun espace n'est autorisé." + custom_emoji: + attributes: + name: + taken: est déjà utilisé pas un autre emoji + topic_status_update: + attributes: + execute_at: + in_the_past: "doit être dans le futur" <<: *errors user_profile: - no_info_me: "
    Le champs À propos de moi de votre profil est vide, voulez vous le remplir ?
    " + no_info_me: "
    Le champs À propos de moi de votre profil est vide, voulez vous le remplir ?
    " no_info_other: "
    %{name} n'a pas encore renseigné le champ À propos de moi de son profil
    " vip_category_name: "Salon" vip_category_description: "Une catégorie réservée aux membres avec un niveau de confiance 3 et plus." @@ -384,6 +410,7 @@ fr: pms_per_day: "Vous avez atteint le nombre maximum de nouveaux messages pour aujourd'hui. Patientez s'il vous plaît %{time_left} avant d'essayer à nouveau." create_like: "Vous avez atteint le nombre maximum de J'aime pour aujourd'hui. Patientez s'il vous plaît %{time_left} avant d'essayer à nouveau." create_bookmark: "Vous avez atteint le nombre maximum de favoris pour aujourd'hui. Patientez s'il vous plaît %{time_left} avant d'essayer à nouveau." + edit_post: "Vous avez atteint le nombre maximum de modifications pour aujourd'hui. Veuillez patienter %{time_left} avant d'essayer à nouveau." live_post_counts: "Vous demandez le nombre de posts en activité trop rapidement. Veuillez respecter le temps d'attente %{time_left} avant de réessayer." unsubscribe_via_email: "Vous avez atteint le nombre maximum de désinscriptions par email pour aujourd'hui. Patientez s'il vous plaît %{time_left} avant d'essayer à nouveau." topic_invitations_per_day: "Vous avez atteint le nombre maximum d'invitations dans un sujet pour aujourd'hui. Patientez s'il vous plaît %{time_left} avant d'essayer à nouveau." @@ -493,6 +520,13 @@ fr: welcome_to: "Bienvenue sur %{site_name} !" approval_required: "Un modérateur doit approuver manuellement votre nouveau compte avant que vous accédiez au forum. Vous recevrez un courriel lorsque que votre compte sera approuvé !" missing_session: "Nous ne pouvons pas détecter si votre compte a été créé, veuillez vérifier que vous avez activé les cookies." + activated: "Désolé, ce compte a déjà été activé." + admin_confirm: + title: "Confirmer le compte administrateur" + description: "Êtes-vous sûr de vouloir %{target_username} comme administrateur ?" + grant: "Accorder l'accès administrateur" + complete: "%{target_username} est maintenant un administrateur." + back_to: "Retourner à %{title}" post_action_types: off_topic: title: 'Hors-sujet' @@ -891,6 +925,7 @@ fr: login_required: "Authentification requise pour lire le contenu du site, interdit l'accès anonyme." min_password_length: "Longueur minimale du mot de passe." min_admin_password_length: "Longueur minimale du mot de passe pour l'administrateur." + password_unique_characters: "Nombre minimum de caractères uniques qu'un mot de passe doit avoir." block_common_passwords: "Ne pas autoriser les mots de passe qui font partie des 10 000 les plus utilisés." enable_sso: "Activer l'authentification unique via un site externe (ATTENTION : LES ADRESSES COURRIEL *DOIVENT* ÊTRE VALIDÉES PAR LE SITE EXTERNE !)" verbose_sso_logging: "Conserver les diagnostics liés au SSO dans /logs" @@ -953,6 +988,7 @@ fr: max_private_messages_per_day: "Nombre maximum de messages que les utilisateurs peuvent créer chaque jour." max_invites_per_day: "Nombre maximum d'invitations qu'un utilisateur peut envoyer par jour." max_topic_invitations_per_day: "Nombre maximum d'invitations à un sujet qu'un utilisateur peut envoyer par jour." + max_logins_per_ip_per_hour: "Nombre maximum de connexions autorisées par adresse IP et par heure" max_logins_per_ip_per_minute: "Nombre maximum de connexions autorisées par adresse IP, par minute" alert_admins_if_errors_per_minute: "Nombre d'erreurs par minute nécessaires pour déclencher une alerte administrateur. Une valeur de 0 désactive cette fonctionnalité. N. B. : nécessite un redémarrage." alert_admins_if_errors_per_hour: "Nombre d'erreurs par heure nécessaires pour déclencher une alerte administrateur. Une valeur de 0 désactive cette fonctionnalité. N. B. : nécessite un redémarrage." @@ -1066,6 +1102,7 @@ fr: max_age_unmatched_ips: "Effacer les adresses IP sous surveillenace sans correspondance après (N) jours" num_flaggers_to_close_topic: "Nombre minimum de signalements uniques requis pour automatiquement suspendre un sujet pour intervention" num_flags_to_close_topic: "Nombre minimum de signalements uniques requis pour automatiquement suspendre un sujet pour intervention" + num_hours_to_close_topic: "Nombre d'heures de fermeture d'un sujet pour intervention." auto_respond_to_flag_actions: "Activer la réponse automatique lors du traitement d'un signalement." min_first_post_typing_time: "Minimum de temps en millisecondes qu'un utilisateur doit passer à la saisie de son premier commentaire, si le seuil n'est pas atteint, il rejoindra automatiquement la file des commentaires en cours d'approbation. Mettre à 0 pour désactiver (non recommandé)" auto_block_fast_typers_on_first_post: "Bloque automatiquement les utilisateurs qui n'ont pas respecté min_first_post_typing_time" @@ -1128,6 +1165,7 @@ fr: digest_posts: "Le nombre maximum de messages populaires à afficher dans le résumé par courriel." digest_other_topics: "Le nombre maximum de sujets à afficher dans la section « Nouveautés dans les sujets et catégories que vous suivez » du résumé par courriel." digest_min_excerpt_length: "Longueur minimale (en caractères) de l'extrait des messages dans le résumé par courriel." + suppress_digest_email_after_days: "Ne pas envoyer de résumés par courriel aux utilisateurs qui n'ont pas visité le site depuis plus de (n) jours." digest_suppress_categories: "Ne pas inclure ces catégories dans les résumés par courriel." disable_digest_emails: "Désactiver les résumés par courriel pour tous les utilisateurs." email_accent_bg_color: "La couleur d'accentuation utilisée comme arrière-plan de certains éléments des courriels HTML. Entrez un nom de couleur (« red ») ou une valeur hexadécimale (« #FF0000 »)." @@ -1146,6 +1184,7 @@ fr: user_website_domains_whitelist: "Les sites Web des utilisateurs vont être vérifiés contre ces domaines. Liste délimitée par des pipes (|)." allow_profile_backgrounds: "Autoriser les utilisateurs à envoyer des arrières-plans de profil." sequential_replies_threshold: "Nombre de messages successifs qu'un utilisateur peut poster dans un sujet avant d'être averti d'avoir posté un nombre excessif de réponses qui se suivent." + get_a_room_threshold: "Nombre de messages qu'un utilisateur doit faire à la même personne dans un même sujet avant d'être averti." enable_mobile_theme: "Les appareils mobiles utilisent un thème adapté aux mobiles, avec la possibilité de passer à la totalité du site. Désactivez cette option si vous voulez utiliser une feuille de style personnalisée qui répond à tous les types de client." dominating_topic_minimum_percent: "Quel est le pourcentage de messages un utilisateur doit poster dans un sujet avant d'être rappelé à l'ordre pour laissé la communauté répondre." disable_avatar_education_message: "Désactiver le message incitant à changer l'avatar." @@ -1157,6 +1196,7 @@ fr: read_time_word_count: "Nombre de mots par minute servant de base de calcul à l'estimation du temps de lecture." topic_page_title_includes_category: "Le titre de la page du sujet inclut le nom de la catégorie." native_app_install_banner: "Propose aux visiteurs réguliers d'installer l'application Discourse native." + share_anonymized_statistics: "Partager les statistiques d'utilisation anonymes." max_prints_per_hour_per_user: "Nombre maximum d'accès à la page /print (mettre à 0 pour désactiver)" full_name_required: "Le nom complet est requis dans le profil utilisateur." enable_names: "Autoriser l'affichage des noms complets des utilisateurs dans leur profil, sur leur carte d'utilisateur et dans les courriels. Décocher pour cacher les noms complets partout." @@ -1272,10 +1312,13 @@ fr: topic: 'Résultats' user: 'Utilisateurs' sso: + login_error: "Erreur de connexion" not_found: "Votre compte n'a pas été trouvé. Merci de contacter l'administrateur du site." account_not_approved: "Votre compte est en attente d'approbation. Vous recevrez une notification par courriel lorsque vous êtes approuvé." unknown_error: "Il y a un problème avec votre compte. Merci de contacter l'administrateur du site." timeout_expired: "La connexion au compte a expiré, veuillez essayer de vous reconnecter." + no_email: "Aucune adresse de courriel n'a été fournie. Veuillez contacter l'administrateur du site." + email_error: "Un compte n'a pas pu être enregistré avec l'adresse de courriel %{email}. Veuillez contacter l'administrateur du site." original_poster: "Créateur du sujet" most_posts: "Le plus de messages" most_recent_poster: "Auteur le plus récent" @@ -1329,6 +1372,24 @@ fr: autoclosed_enabled_lastpost_minutes: one: "Cette discussion a été automatiquement fermée une minute après le dernier message. Aucune réponse n'est permise dorénavant." other: "Ce sujet a été automatiquement fermé %{count} minutes après le dernier message. Aucune réponse n'est permise dorénavant." + autoclosed_disabled_days: + one: "Ce sujet a été automatiquement ouvert après 1 jour." + other: "Ce sujet a été automatiquement ouvert après %{count} jours." + autoclosed_disabled_hours: + one: "Ce sujet a été automatiquement ouvert après 1 heure." + other: "Ce sujet a été automatiquement ouvert après %{count} heures." + autoclosed_disabled_minutes: + one: "Ce sujet a été automatiquement ouvert après 1 minute." + other: "Ce sujet a été automatiquement ouvert après %{count} minutes." + autoclosed_disabled_lastpost_days: + one: "Ce sujet a été automatiquement ouvert 1 jour après la dernière réponse." + other: "Ce sujet a été automatiquement ouvert %{count} jours après la dernière réponse." + autoclosed_disabled_lastpost_hours: + one: "Ce sujet a été automatiquement ouvert 1 heure après la dernière réponse." + other: "Ce sujet a été automatiquement ouvert %{count} heures après la dernière réponse." + autoclosed_disabled_lastpost_minutes: + one: "Ce sujet a été automatiquement ouvert 1 minute après la dernière réponse." + other: "Ce sujet a été automatiquement ouvert %{count} minutes après la dernière réponse." autoclosed_disabled: "Ce sujet est maintenant ouvert. Les nouvelles réponses sont autorisées." autoclosed_disabled_lastpost: "Ce sujet est maintenant ouvert. Vous pouvez y participer." pinned_enabled: "Ce sujet est maintenant épinglé. Il apparaîtra en haut de sa catégorie jusqu'à ce qu'il soit désépinglé par un modérateur, ou individuellement par les utilisateurs eux-mêmes." @@ -1342,7 +1403,6 @@ fr: incorrect_username_email_or_password: "pseudo, adresse de courriel ou mot de passe incorrect" wait_approval: "Merci de vous être inscrit. Nous vous informerons lorsque votre compte aura été approuvé." active: "Votre compte est activé et prêt à l'emploi." - activate_email: "

    Vous avez presque fini ! Un courriel de confirmation a été envoyé à %{email}. Suivez les instructions pour activer votre compte.

    S'il ne vous parvient pas, vérifiez le répertoire des courriers indésirables ou essayez de vous reconnecter pour envoyer une nouvelle demande d'activation.

    " not_activated: "Vous ne pouvez pas vous connecter pour le moment. Nous vous avons envoyé un courriel d'activation. Merci de suivre les instructions qui s'y trouve pour activer votre compte." not_allowed_from_ip_address: "Vous ne pouvez pas vous connecter en tant que %{username} depuis cette adresse IP." admin_not_allowed_from_ip_address: "Vous ne pouvez pas vous connecter depuis cette adresse IP." @@ -1366,6 +1426,8 @@ fr: deactivated: "A été désactivé à cause de trop de courriels rejetés vers '%{email}'." deactivated_by_staff: "Désactivé par un responsable" activated_by_staff: "Activé par un responsable" + new_user_typed_too_fast: "Le nouvel utilisateur écrivait trop rapidement" + content_matches_auto_block_regex: "Le contenu correspond à l'expression régulière de blocage automatique" username: short: "doit être d'au moins %{min} caractères" long: "ne doit pas être supérieur à %{max} caractères" @@ -1404,13 +1466,6 @@ fr: subject_template: "%{invitee_name} vous a invité(e) à rejoindre %{site_domain_name}" invite_password_instructions: subject_template: "Renseignez le mot de passe pour votre compte utilisateur %{site_name} " - text_body_template: | - Merci d'avoir accepté l'invitation sur %{site_name} -- Bienvenue ! - - Cliquer sur ce lien pour choisir un mot de passe maintenant : - %{base_url}/users/password-reset/%{email_token} - - (Si le lien est expiré, choisissez "J'ai oublié mon mot de passe" lorsque vous essayez de vous connecter avec votre adresse mail.) test_mailer: text_body_template: | Ceci est un courriel de test de @@ -1498,7 +1553,6 @@ fr: disagreed: "Merci de nous en informer. Nous travaillons à sa résolution." deferred: "Merci de nous en informer. Nous travaillons à sa résolution." deferred_and_deleted: "Merci de nous en informer. Nous avons supprimé le message." - temporarily_closed_due_to_flags: "Ce sujet est temporairement fermé à cause d'un grand nombre de signalements de la communauté." system_messages: post_hidden: title: "Message caché" @@ -1705,6 +1759,7 @@ fr: S'il y a une interface web pour le compte POP, vous devrez peut-être vous y connecter pour vérifier les paramètres. too_many_spam_flags: + title: "Trop de signalements de spam" subject_template: "Nouveau compte bloqué" text_body_template: | Bonjour, @@ -1885,6 +1940,7 @@ fr: %{respond_instructions} user_linked: + title: "Utilisateur lié" text_body_template: | %{header_instructions} @@ -1913,6 +1969,7 @@ fr: %{respond_instructions} user_posted: + title: "Posté par l'utilisateur" text_body_template: | %{header_instructions} @@ -1972,50 +2029,10 @@ fr: back_to_top: "Aller en haut" forgot_password: title: "Mot de passe oublié" - text_body_template: | - Quelqu'un a demandé la réinitialisation de votre mot de passe sur [%{site_name}](%{base_url}). - - Si ce n'était pas vous, vous pouvez ignorer ce courriel en toute sécurité. - - Cliquez sur le lien ci-dessous pour choisir un nouveau mot de passe : - %{base_url}/users/password-reset/%{email_token} set_password: title: "Définir le mot de passe" - text_body_template: | - Quelqu'un a demandé d'ajouter un mot de passe à votre compte sur [%{site_name}](%{base_url}). Alternativement, vous pouvez vous connecter en utilisant un service en ligne pris en charge (Google, Facebook, etc) déjà associé à cette adresse de courriel validée. - - Si vous n'avez pas fait cette demande, vous pouvez ignorer ce courriel. - - Cliquez sur le lien suivant pour choisir un mot de passe: - %{base_url}/users/password-reset/%{email_token} admin_login: - text_body_template: | - On a cherché à se connecter à votre compte sur [%{site_name}](%{base_url}). - - Si ce n'est pas vous, ignorez sans crainte ce courriel. - - Sinon, cliquez sur ce lien pour vous connecter : - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Un nouveau compte utilisateur a été créé pour vous sur {site_name} - - Cliquez sur le lien ci-dessous pour choisir un mot de passe pour votre nouveau compte : - %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - text_body_template: | - Confirmer votre nouvelle adresse courriel pour %{site_name} en cliquant sur le lien suivant : - - %{base_url}/users/authorize-email/%{email_token} - confirm_old_email: - text_body_template: | - Avant de pouvoir changer votre adresse courriel, nous devons confirmer que vous contrôlez - bien l'adresse actuelle. Après avoir effectuée cette étape, vous devrez confirmer - la nouvelle adresse courriel. - - Confirmer votre adresse courriel actuelle pour %{site_name} en cliquant sur le lien suivant : - - %{base_url}/users/authorize-email/%{email_token} + title: "Identifiant Administrateur" notify_old_email: text_body_template: | Ceci est un message automatique pour vous informer que votre adresse courriel pour @@ -2028,31 +2045,8 @@ fr: signup_after_approval: title: "Inscription après approbation" subject_template: "Votre compte a été approuvé sur %{site_name} !" - text_body_template: | - Bienvenue sur %{site_name} ! - - Un membre de l'équipe a approuvé votre compte sur %{site_name}. - - Cliquez sur le lien suivant pour confirmer et activer votre nouveau compte : %{base_url}/users/activate-account/%{email_token} - - Si le lien ci-dessus n'est pas cliquable, essayez de le copier-coller dans la barre d'adresse de votre navigateur web. - - %{new_user_tips} - - Nous croyons au [comportement communautaire civilisé](%{base_url}/guidelines) en tous temps. - - Amusez-vous bien ! - - (Si, en tant que nouvel utilisateur, vous avez besoin de communiquer avec un [responsable](%{base_url}/about), répondez simplement à ce message.) signup: title: "Inscription" - text_body_template: | - Bienvenue sur %{site_name} ! - - Cliquez sur le lien suivant pour confirmer et activer votre nouveau compte : - %{base_url}/users/activate-account/%{email_token} - - Si le lien ci-dessus n'est pas cliquable, essayez de le copier et coller dans la barre d'adresse de votre navigateur web. page_not_found: title: "Oups ! Cette page n'existe pas ou est privée." popular_topics: "Populaires" @@ -2403,7 +2397,6 @@ fr: Ce badge est accordé la première fois que vous ajoutez un Emoji à votre message :thumbsup:. Les Emoji vous permettent de transmettre de l'émotion dans vos messages, du bonheur :smiley: à la tristesse :anguished: en passant par la colère :angry: et tout le reste :sunglasses:. Il suffit de taper : (deux-points) ou d'appuyer sur le bouton de la barre d'outils Emoji dans l'éditeur et de choisir parmi des centaines de choix :ok_hand: first_mention: name: Première mention - description: A mentionné un utilisateur dans un message long_description: Ce badge est accordé la première fois que vous mentionnez le @pseudo de quelqu'un dans votre message. Chaque mention génère une notification à cette personne pour qu'elle soit informée de votre message. Il suffit de commencer à taper @ (arobase) pour mentionner un utilisateur ou, si autorisé, un groupe – c'est un moyen pratique de porter quelque chose à leur attention. first_onebox: name: Premier onebox @@ -2411,7 +2404,6 @@ fr: long_description: Ce badge est accordé la première fois que vous publiez un lien seul sur une ligne, qui a ensuite été développé automatiquement dans un onebox avec un bref résumé du lien, un titre, et (le cas échéant) une image. first_reply_by_email: name: Première réponse par courriel - description: A répondu à un message par courriel long_description: | Ce badge est accordé la première fois que vous répondez à un message par courriel :e-mail:. admin_login: @@ -2445,7 +2437,6 @@ fr: safe_mode: title: "Activer le mode sans échec" description: "Le mode sans échec vous permet de tester votre site sans charger les extensions ou personnalisations." - no_customizations: "Désactiver toutes les personnalisations" only_official: "Désactiver les extensions non officielles" no_plugins: "Désactiver toutes les extensions" enter: "Activer le mode sans échec" diff --git a/config/locales/server.gl.yml b/config/locales/server.gl.yml index 8d79c78cdf..c173d9b36a 100644 --- a/config/locales/server.gl.yml +++ b/config/locales/server.gl.yml @@ -421,7 +421,6 @@ gl: disagreed: "Grazas por comunicárnolo. Estamos botándolle un ollo." deferred: "Grazas por comunicárnolo. Estamos botándolle un ollo." deferred_and_deleted: "Grazas por comunicárnolo. Xa eliminamos a publicación." - temporarily_closed_due_to_flags: "Este tema está pechado temporalmente polo gran número de denuncias da comunidade." system_messages: welcome_user: subject_template: "Benvido/a a %{site_name}!" diff --git a/config/locales/server.he.yml b/config/locales/server.he.yml index b43cfd7fb5..f0d699d34e 100644 --- a/config/locales/server.he.yml +++ b/config/locales/server.he.yml @@ -27,12 +27,15 @@ he: topics: "נושאים" posts: "פוסטים" loading: "טוען" - powered_by_html: 'מונע ע"י Discourse, פועל מיטבית עם Javascript מאופשר' + powered_by_html: 'מופעל על ידי דיסקורס, לצפיה מיטבית יש להפעיל JavaScript' log_in: "התחברות" purge_reason: "נמחק אוטומטית כחשבון נטוש ולא פעיל" disable_remote_images_download_reason: "הורדת תמונות מרחוק נחסמה בשל היעדר מספיק שטח אחסון פנוי." anonymous: "אנונימי" remove_posts_deleted_by_author: "נמחק על ידי הכותב" + themes: + bad_color_scheme: "לא ניתן לעדכן את התמה, סכמת צבעים שגויה" + other_error: "ארעה שגיאה בזמן עדכון התמה" emails: incoming: default_subject: "נושא זה צריך כותרת" @@ -344,6 +347,7 @@ he: in_the_past: "חייב להיות בעתיד." <<: *errors user_profile: + no_info_me: "
    השדה ״אודותי״ בפרופיל שלכם הוא ריק כרגע, תרצו למלא אותו?
    " no_info_other: "
    %{name} עדיין לא הזין/ה דבר בשדה אודות של הפרופיל שלו/ה
    " vip_category_name: "לאונג'" vip_category_description: "קטגוריה אקסקלוסיבית למשתמשים עם רמת אמון 3 או יותר." @@ -1251,7 +1255,7 @@ he: embed_username_required: "נדרש שם משתמש ליצירת הנושא." embed_whitelist_selector: "בוררי CSS לאלמנטים שיותר להטמיע." embed_blacklist_selector: "בוררי CSS לאלמנטים שיוסרו מן ההטמעות." - notify_about_flags_after: "אם יש דגלים שלא טופלו לאחר כמות זו של שעות, שילחו אימייל ל contact_email. קבעו 0 לניטרול." + notify_about_flags_after: "אם יש דגלים שלא טופלו לאחר מספר זה של שעות, שילחו הודעה פרטית לצוות. קיבעו ל 0 כדי לנטרל." show_create_topics_notice: "אם לאתר פחות מ-5 נושאים פומביים, הציגו מודעה המבקשת מן המנהלים ליצור עוד נושאים." delete_drafts_older_than_n_days: מחקו טיוטות בנות יותר מ (n) ימים. bootstrap_mode_min_users: "מספר משתמשים מינימלי שנדרש כדי לנטרל מצב איתחול (קבעו ל 0 כדי לנטרל)" @@ -1413,6 +1417,15 @@ he: autoclosed_disabled_minutes: one: "נושא זה נפתח אוטומטית לאחר דקה 1." other: "נושא זה נפתוח אוטומטית לאחר %{count} דקות." + autoclosed_disabled_lastpost_days: + one: "הנושא נפתח אוטומטית יום 1 אחרי התגובה האחרונה." + other: "הנושא נפתח אוטומטית %{count} ימים אחרי התגובה האחרונה." + autoclosed_disabled_lastpost_hours: + one: "נושא זה נפתח אוטומטית שעה 1 אחרי התגובה האחרונה." + other: "נושא זה נפתח אוטומטית %{count} שעות אחרי התגובה האחרונה." + autoclosed_disabled_lastpost_minutes: + one: "נושא זה נפתח אוטומטית דקה 1 אחרי התגובה האחרונה." + other: "נושא זה נפתח אוטומטית %{count} דקות אחרי התגובה האחרונה." autoclosed_disabled: "הנושא הזה נפתח. ניתן להגיב תגובות חדשות." autoclosed_disabled_lastpost: "הנושא הזה נפתח. ניתן להגיב תגובות חדשות." pinned_enabled: "הנושא הזה ננעץ. הוא יופיע בראש הקטגוריה שלו עד שיוסר מנעיצה על ידי מנהל או שכפתור נקה נעיצות נלחץ." @@ -1426,7 +1439,7 @@ he: incorrect_username_email_or_password: "שם משתמש, דואר אלקטרוני או סיסמה לא נכונים" wait_approval: "תודה על שנרשמת. אנחנו ניידע אותך כשהחשבון שלך יאושר." active: "החשבון שלך הופעל ומוכן לשימוש." - activate_email: "

    כמעט סיימנו! שלחנו מייל אקטיבציה אל %{email}. אנא עקבו אחר ההנחיות במייל כדי להפעיל את חשבונכם.

    אם המייל לא יגיע, בדקו את תיקיית הספאם שלכם, או נסו להתחבר פעם נוספת כדי לשלוח מייל אקטיביזציה נוסף.

    " + activate_email: "

    כמעט סיימתם! שלחנו מייל הפעלה ל-%{email}. בבקשה עיקבו אחרי ההוראות במייל כדי להפעיל את החשבון שלכם.

    אם הוא לא מגיע, בידקו בתיקיית הספאם, או נסו להתחבר שוב כדי לשלוח עוד מייל הפעלה או להכניס כתובת מייל אחרת.

    " not_activated: "אינכם יכולים להתחבר עדיין. שלחנו לכם הודעת דואר אלקטרוני לאישור. בבקשה עיקבו אחר ההוראות במייל כדי להפעיל את חשבונכם." not_allowed_from_ip_address: "אינכם יכולים להתחבר כ-%{username} מכתובת IP זו. " admin_not_allowed_from_ip_address: "אינכם יכולים להתחבר כמנהלי מערכת מכתובת IP זו." @@ -1541,7 +1554,7 @@ he: %{invite_link} custom_invite_forum_mailer: - title: "שולח-מיילים להזמנה לפורום" + title: "דואר הזמנה לפורום מותאם אישית" subject_template: "%{invitee_name} הזמין/ה אתכם להצטרף ל %{site_domain_name}" text_body_template: | %{invitee_name} הזמין/ה אתכם להצטרף @@ -1561,9 +1574,12 @@ he: title: "הנחיות סיסמה למוזמנים" subject_template: "צרו סיסמה עבור חשבון ה-%{site_name} שלכם." text_body_template: | - תודה שקיבלתם את ההזמנה שלכם ל %{site_name} -- ברוכים הבאים! - לחצו על הקישור בשביל לבחור סיסמה: %{base_url}/users/password-reset/%{email_token} - (אם הקישור פג תוקף, בחרו ב"שכחתי את הסיסמה שלי" בהתחברות.) + תודה שקיבלתם את ההזמנה ל-%{site_name} -- ברוכים הבאים! + + לחצו על קישור זה כדי לבחור סיסמה עכשיו: + %{base_url}/u/password-reset/%{email_token} + + (אם אתם חושבים שהקישור למעלה פקע, בחרו ב״שכחתי את הסיסמה שלי״ כשאתם מתחברים עם כתובת המייל שלכם.) download_backup_mailer: title: "הורדת גיבוי שולח מיילים" subject_template: "הורדת גיבוי אתר [%{email_prefix}]" @@ -1575,6 +1591,13 @@ he: (אם *לא* ביקשתם הורדה זו, אתם אמורים להיות מוטרדים למדי -- למישהו יש הרשאות אדמיניסטרציה לאתר שלכם.) no_token: | מצטערים, כבר נעשה שימוש בקישור הורדת גיבוי זה או שהוא פקע. + admin_confirmation_mailer: + title: "אישור אדמיניסטרטור" + subject_template: "[%{email_prefix}] אישור חשבון חדש" + text_body_template: | + בבקשה אשרו שהייתם מעוניינים להוסיף את **%{target_username}** כאדמיניסטרטור של הפורום שלכם. + + [Confirm Administrator Account](%{admin_confirm_url}) test_mailer: title: "שולח-מיילים לבדיקה" subject_template: "[%{email_prefix}] מייל בדיקת שליחתיות" @@ -1671,7 +1694,9 @@ he: disagreed: "תודה שעדכנת אותנו. אנחנו בודקים את זה." deferred: "תודה שעדכנתם אותנו. אנחנו בודקים את העניין." deferred_and_deleted: "תודה שעדכנתם אותנו. הסרנו את הפוסט." - temporarily_closed_due_to_flags: "נושא זה סגור באופן זמני בעקבות מספר רב של דגלים של הקהילה. " + temporarily_closed_due_to_flags: + one: "נושא זה סגור זמנית לשעה 1 עקב מספר רב של דגלי קהילה." + other: "נושא זה סגור זמנית ל-%{count} שעות עקב מספר רב של דגלי קהילה." system_messages: post_hidden: title: "פוסט חבוי" @@ -1692,9 +1717,9 @@ he: להנחיות נוספות, אנא פנו ל[הנחיות הקהילה](%{base_url}/guidelines). usage_tips: text_body_template: | - כמה טיפים לגבי איך להתחיל כמשתמשים חדשים, [ראו את הפוסט הזה](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). + לכמה טיפים זריזים בנוגע להתחלה כמשתמשים חדשים, [קיראו פוסט זה](https://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). - ככל שתשתתפו כאן, נכיר אתכם, ומגבלות זמניות על משתמשים חדשים יוסרו. במהלך הזמן תצבעו [דרגות אמון](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) שכוללות יכולות לסייע לנו לנהל את הקהילה שלנו ביחד. + ככל שתשתתפו כאן, נכיר אתכם, והגבלות זמניות של משתמשים חדשים יוסרו. במשך הזמן תצברו [רמות אמון](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) שיכללו יכולות מיוחדות שיעזרו לנו לנהל את הקהילה שלנו יחד. welcome_user: title: "ברוכים הבאים למשתמש/ת" subject_template: "ברוכים הבאים ל %{site_name}!" @@ -2040,6 +2065,14 @@ he: new_user_of_the_month: title: "אתם המשתמשים החדשים של החודש!" subject_template: "אתם המשתמשים החדשים של החודש!" + text_body_template: | + ברכותינו, הוענק לכם **פרס המשתמש החדש של החודש עבור %{month_year}**. :trophy: + + הפכתם במהרה לחברים מוערכים בקהילה שלנו וכולם ממש נהנים מהפוסטים שלכם. + + פרס זה יוצג בקביעות כעיטור ב[דף המשתמש שלכם](/my/badges). + + תודה, והמשיכו עם העבודה המעולה! unsubscribe_link: | כדי להפסיק את המיילים האלו, [לחצו כאן](%{unsubscribe_url}). unsubscribe_link_and_mail: | @@ -2249,56 +2282,57 @@ he: title: "שכח/ה סיסמה" subject_template: "[%{email_prefix}] איפוס סיסמה" text_body_template: | - מישהו ביקש לאפס את הסיסמה שלכם ב [%{site_name}](%{base_url}). + מישהו ביקש לאפס את הסיסמה שלכם ב-[%{site_name}](%{base_url}). - אם זה לא הייתם אתם, אתם יכולים להתעלם ממייל זה בביטחה. + אם זה לא הייתם אתם, אתם יכולים פשוט להתעלם ממייל זה. לחצו על הקישור הבא כדי לבחור סיסמה חדשה: - %{base_url}/users/password-reset/%{email_token} + %{base_url}/u/password-reset/%{email_token} set_password: title: "קביעת סיסמה" subject_template: "[%{email_prefix}] קביעת סיסמה" text_body_template: | - מישהו ביקש להוסיף סיסמה לחשבון שלכם ב [%{site_name}](%{base_url}). לחילופין, תוכלו להתחבר באמצעות כל שירות נתמך (גוגל, פייסבוק, וכד׳) שמקושר לכתובת מייל תקינה. + מישהו ביקש להוסיף סיסמה לחשבון שלכם ב-[%{site_name}](%{base_url}). לחילופין, אתם יכולים להתחבר באמצעות כל שירות מקוון נתמך (גוגל, פייסבוק, וכד׳) שמקושר עם כתובת מייל זו. - אם לא אתם ביקשתם זאת, תוכלו להתעלם ממייל זה ללא חשש. + אם לא אתם ביקשתם זאת, אתם יכולים פשוט להתעלם ממייל זה. - לחצו על הלינק הבא כדי לבחור סיסמה: - %{base_url}/users/password-reset/%{email_token} + לחצו על הקישור הבא כדי לבחור סיסמה: + %{base_url}/u/password-reset/%{email_token} admin_login: title: "התחברות אדמיניסטרטור" subject_template: "[%{email_prefix}] התחברות" text_body_template: | - מישהו ביקש להתחבר לחשבונכם ב [%{site_name}](%{base_url}). - אם לא ביקשתם זאת, תוכלו להתעלם מהמייל הזה. - לחצו על הקישור הבא להתחבר: - %{base_url}/users/admin-login/%{email_token} + מישהו ניסה להתחבר לחשבון שלכם ב-[%{site_name}](%{base_url}). + + אם לא אתם ביקשתם זאת, אתם יכולים פשוט להתעלם ממייל זה. + + לחצו על הקישור הבא כדי להתחבר: + %{base_url}/u/admin-login/%{email_token} account_created: title: "חשבון נוצר" subject_template: "[%{email_prefix}] החשבון החדש שלך" text_body_template: | - חשבון חדש נוצר עבורך ב%{site_name} + חשבון חדש נוצר עבורכם ב-%{site_name} - הקישו על הקישור המצורף כדי להגדיר סיסמה לחשבונכם החדש: - %{base_url}/users/password-reset/%{email_token} + לחצו על הקישור הבא כדי לבחור סיסמה לחשבון החדש שלכם: + %{base_url}/u/password-reset/%{email_token} confirm_new_email: title: "אישור מייל חדש" subject_template: "[%{email_prefix}] אשרו את כתובת המייל החדשה שלכם" text_body_template: | אשרו את כתובת המייל החדשה שלכם עבור %{site_name} על ידי לחיצה על הקישור הבא: - %{base_url}/users/authorize-email/%{email_token} + %{base_url}/u/authorize-email/%{email_token} confirm_old_email: title: "אישור מייל ישן" subject_template: "[%{email_prefix}] אשרו את כתובת המייל הנוכחית שלכם" text_body_template: | - לפני שתוכלו להחליף את כתובת המייל שלכם, אנחנו צריכים שתאשרו שאתם שולטים - בכתובת המייל הנוכחית. אחרי שאתם משלימים את הצעד הזה, אנחנו נבקש שתאשרו - את כתובת המייל החדשה. + לפני שנוכל לשנות את כתובת המייל שלכם, אנחנו צריכים שתאשרו שאתם שולטים + בחשבון המייל הנוכחי. אחרי שתשלימו שלב זה, נבקש שתאשרו את כתובת המייל החדשה. - אשרו את כתובת המייל הנוכחית שלכם עבור %{site_name} על ידי לחיצה על הקישור הבא: + אשרו את כתובת המייל הנוכחית עבור %{site_name} על ידי לחיצה על הקישור הבא: - %{base_url}/users/authorize-email/%{email_token} + %{base_url}/u/authorize-email/%{email_token} notify_old_email: title: "התראת מייל ישן" subject_template: "[%{email_prefix}] כתובת המייל שלכם שונתה" @@ -2312,32 +2346,32 @@ he: title: "הרשמה אחרי אישור" subject_template: "אושרתם באתר %{site_name}!" text_body_template: | - ברוכים הבאים ל%{site_name}! + ברוכים הבאים ל-%{site_name}! - חבר צוות אישר את החשבון שלכם ב %{site_name}. + חבר צוות אישר את החשבון שלכם ב-%{site_name}. - לחצו על הקישור הבא לאשר והפעיל את החשבון החדש שלכם: - %{base_url}/users/activate-account/%{email_token} + לחצו על הקישור הבא כדי לאשר ולהפעיל את החשבון החדש שלכם: + %{base_url}/u/activate-account/%{email_token} - אם הלינק לא לחיץ, נסו להעתיק ולהדביק אותו לסרגל הכתובת בראש הדפדפן. + אם הקישור למעלה אינו לחיץ, נסו להעתיק ולהדביק אותו בשורת הכתובת של הדפדפן שלכם. %{new_user_tips} - אנו מאמינים ב[התנהגות קהילתית מתורבתת](%{base_url}/guidelines) בכל זמן. + אנחנו מאמינים ב-[התנהגות קהילתית מתורבתת](%{base_url}/guidelines) בכל זמן שהוא. תהנו מהביקור! - (אם אתם צריכים ליצור קשר עם [חברי צוות](%{base_url}/about) כחברים חדשים, רק השיבו להודעה זאת.) + (אם אתם צריכים ליצור קשר עם [חברי צוות](%{base_url}/about) כמשתמשים חדשים, פשוט השיבו להודעה זו.) signup: title: "הרשמה" subject_template: "[%{email_prefix}] אשרו את חשבונכם החדש" text_body_template: | - ברוכים הבאים ל %{site_name}! + ברוכים הבאים ל-%{site_name}! לחצו על הקישור הבא כדי לאשר ולהפעיל את החשבון החדש שלכם: - %{base_url}/users/activate-account/%{email_token} + %{base_url}/u/activate-account/%{email_token} - אם הקישור למעלה אינו לחיץ, נסו להעתיק ולהדביק אותו לשורת הכתובת של הדפדפן שלכם. + אם הקישור למעלה אינו לחיץ, נסו להעתיק ולהדביק אותו בשורת הכתובת של הדפדפן שלכם. page_not_found: title: "אופס! הדף לא קיים או שהוא פרטי." popular_topics: "פופלארי" @@ -2391,6 +2425,8 @@ he: no_echo_mailing_list_mode: "התראות רשימת תפוצה ינוטרלו עבור הפוסטים של המשתמש עצמו" color_schemes: base_theme_name: "בסיס" + default: "סכמה בהירה" + dark: "סכמה כהה" about: "אודות" guidelines: "קווים מנחים" privacy: "פרטיות" @@ -2847,6 +2883,9 @@ he: new_user_of_the_month: name: "המשתמש/ת החדש/ה של החודש" description: תרומות יוצאות מהכלל בחודש הראשון שלהם + long_description: | + עיטור זה מוענק כדי לברך שני משתמשים חדשים בכל חודש בעבור תרומות מצטיינות שלהם, שנמדדות לפי תדירות הלייקים לפוסטים שלכם, וממי הם ניתנו. + badge_title_metadata: "עיטור ב-%{site_title} של %{display_name}" admin_login: success: "דוא\"ל נשלח" error: "שגיאה!" @@ -2878,7 +2917,7 @@ he: safe_mode: title: "כנסו למצב בטוח" description: "מצב בטוח מאפשר לכם לבחון את האתר שלכם מבלי לטעון תוספים או התאמות אתר." - no_customizations: "נטרלו את כל התאמות האתר" + no_customizations: "ניטרול של תמה נוכחית" only_official: "נטרלו תוספים לא רשמיים" no_plugins: "נטרלו את כל התוספים" enter: "כנסו למצב בטוח" diff --git a/config/locales/server.id.yml b/config/locales/server.id.yml index fe8f48ab51..1ca7b879aa 100644 --- a/config/locales/server.id.yml +++ b/config/locales/server.id.yml @@ -23,7 +23,6 @@ id: topics: "Topik" posts: "post" loading: "Memuat" - powered_by_html: 'Didayakan oleh Discourse, aktifkan JavaScript untuk tampilan terbaik' log_in: "Log In" purge_reason: "Secara otomatis dihapus sebagai akun yang ditinggalkan, dinon-aktifkan." disable_remote_images_download_reason: "Unduh gambar jarak jauh dimatikan karena ruang penyimpanan di server tidak cukup." @@ -198,7 +197,6 @@ id: invalid: "bukan warna yang valid." <<: *errors user_profile: - no_info_me: "
    kolom Tentang Saya dari profil Anda masih kosong, apakah Anda mau mengisinya?
    " no_info_other: "
    %{name} belum mengisi kolom Tentang Saya untuk profilnya
    " vip_category_name: "Lounge / Santai" staff_category_name: "Staf" @@ -456,21 +454,6 @@ id: user_notifications: previous_discussion: "Balasan Sebelumnya" posted_by: "Dipost oleh %{username} pada %{post_date}" - account_created: - text_body_template: |+ - Anda mendapatkan akun baru di %{site_name} - - - Silahkan sentuh tautan dibawah ini untuk menentukan sandi dari akun baru Anda: - - %{base_url}/users/password-reset/%{email_token} - - confirm_new_email: - text_body_template: | - Silahkan mengkonfirmasi email baru Anda di %{site_name} dengan mengunjungi tautan dibawah: - - - %{base_url}/users/authorize-email/%{email_token} page_not_found: see_more: "Selengkapnya" terms_of_service: diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index 40cb2dbb47..500781b23d 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -27,7 +27,6 @@ it: topics: "Argomenti" posts: "messaggi" loading: "Caricamento" - powered_by_html: 'Powered by Discourse, è consigliato abilitare JavaScript ' log_in: "Connetti" purge_reason: "Account cancellato automaticamente perché abbandonato o disattivato" disable_remote_images_download_reason: "Lo scaricamento delle immagini remote è stato disabilitato perché non c'è abbastanza spazio disco disponibile." @@ -110,6 +109,7 @@ it: s3_upload_bucket_is_required: "Non è possibile attivare i caricamenti da S3 a meno che non imposti l'opzione 's3_upload_bucket'." invite: not_found: "Il tuo token di invito non è valido. Per favore contatta l'amministratore del sito." + user_exists: "Non c'è bisogno di invitare %{email}, perché ha già un account!" bulk_invite: file_should_be_csv: "Il file caricato deve essere in formato csv." error: "Si è verificato un errore durante il caricamento del file. Per favore riprova più tardi." @@ -302,16 +302,12 @@ it: attributes: hex: invalid: "non è un colore valido" - post_reply: - base: - different_topic: "Il messaggio e la risposta devono appartenere allo stesso argomento." web_hook: attributes: payload_url: invalid: "La URL non è valida. La URL dovrebbe iniziare per http:// o https:// e nessuno spazio non è ammesso. " <<: *errors user_profile: - no_info_me: "
    il campo \"Su di me\" del tuo profilo è vuoto, vuoi compilarlo?
    " no_info_other: "
    %{name} non ha ancora compilato il campo \"Su di me\" del suo profilo
    " vip_category_name: "Lounge" vip_category_description: "Una categoria esclusiva per i membri con livello di esperienza 3 o superiore." @@ -1171,7 +1167,6 @@ it: incorrect_username_email_or_password: "Nome utente, email o password errati" wait_approval: "Grazie per esserti iscritto. Ti avvertiremo quando il tuo account sarà approvato." active: "Il tuo account è attivo e pronto all'uso." - activate_email: "

    Ci siamo quasi! Abbiamo mandato una email di attivazione a %{email}. Per favore segui le istruzioni contenute nell'email per attivare il tuo account.

    Se l'email non ti arriva, controlla la tua cartella spam o prova a collegarti ancora per inviare un'altra email di attivazione.

    " not_activated: "Non puoi ancora collegarti. Ti abbiamo mandato un'email di attivazione. Per favore segui le istruzioni contenute nell'email per attivare il tuo account." not_allowed_from_ip_address: "Non puoi collegarti come %{username} da quell'indirizzo IP." admin_not_allowed_from_ip_address: "Non puoi collegarti come admin da quell'indirizzo IP." @@ -1218,7 +1213,6 @@ it: disagreed: "Grazie per averci informato. Stiamo provvedendo." deferred: "Grazie per averci informato. Stiamo provvedendo." deferred_and_deleted: "Grazie per averci informato. Abbiamo rimosso il messaggio." - temporarily_closed_due_to_flags: "Questo argomento è temporaneamente chiuso a causa di numerose segnalazioni della comunità." system_messages: post_hidden: subject_template: "Messaggio nascosto a causa di segnalazioni dalla comunità" @@ -1432,63 +1426,8 @@ it: topic_updates: "Aggiornamenti argomento" view_this_topic: "Vedi questo argomento" back_to_top: "Torna in cima" - forgot_password: - text_body_template: | - Qualcuno ha richiesto l'azzeramento della tua password su [%{site_name}](%{base_url}). - - Se non sei stato tu, puoi tranquillamente ignorare questa email. - - Clicca il seguente collegamento per scegliere una nuova password: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Qualcuno ha richiesto di aggiungere una password al tuo account su [%{site_name}](%{base_url}). In alternativa, puoi collegarti usando uno dei servizi online supportati (Google, Facebook ecc.) associato con questo indirizzo email validato. - - Se non hai fatto tu questa richiesta, ignora tranquillamente questa email. - - Clicca sul seguente collegamento per scegliere una password: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - Qualcuno ha richiesto di connettersi al tuo account su [%{site_name}](%{base_url}). - - Se non sei stato tu ad avviare questa richiesta, puoi tranquillamente ignorare questa email. - - Altrimenti, clicca sul seguente collegamento per connetterti: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Ti è stato creato un account su %{site_name} - - Fai clic sul seguente collegamento per scegliere una password per il tuo nuovo account: - %{base_url}/users/password-reset/%{email_token} signup_after_approval: subject_template: "Sei stato ammesso su %{site_name}!" - text_body_template: | - Benvenuto su %{site_name}! - - Un membro dello staff ha approvato il tuo account su %{site_name}. - - Clicca sul seguente collegamento per confermare e attivare il tuo nuovo account: - %{base_url}/users/activate-account/%{email_token} - - Se il collegamento qui sopra non è cliccabile, prova a copiarlo e incollarlo nella barra degli indirizzi del tuo browser. - - %{new_user_tips} - - Noi crediamo in un [comportamento comunitario civile](%{base_url}/guidelines), sempre. - - Buona permanenza! - - ps: se vuoi comunicare privatamente con i [membri dello staff](%{base_url}/about) come nuovo utente, rispondi semplicemente a questo messaggio. - signup: - text_body_template: | - Benvenuto su %{site_name}! - - Clicca sul collegamento seguente per confermare ed attivare il tuo nuovo account: - %{base_url}/users/activate-account/%{email_token} - - Se il collegamento non è cliccabile, copialo ed incollalo manualmente nella barra degli indirizzi del tuo browser. page_not_found: title: "La pagina richiesta non esiste oppure è privata." popular_topics: "Di successo" @@ -1602,6 +1541,9 @@ it: description: Ha ricevuto 2 "Mi piace" su 100 messaggi admired: description: Ha ricevuto 5 "Mi piace" su 300 messaggi + first_emoji: + long_description: | + Questo distintivo viene assegnato la prima volta che inserisci un Emoji in un messaggio :thumbsup:. Gli Emoji ti permettono di trasmettere emozioni nei messaggi, dalla felicità :smiley: alla tristezza :anguished: alla rabbia :angry: e tutto ciò che sta nel mezzo :sunglasses:. Digita semplicemente : (carattere due punti) oppure premi il bottone Emoji nella barra degli strumenti dell'editor, per selezionare tra centinaia di possibilità :ok_hand: admin_login: success: "email Inviata" error: "Errore!" diff --git a/config/locales/server.ja.yml b/config/locales/server.ja.yml index 7c6cb7cd53..71969ce4c7 100644 --- a/config/locales/server.ja.yml +++ b/config/locales/server.ja.yml @@ -25,7 +25,6 @@ ja: topics: "トピック" posts: "投稿" loading: "読み込み中" - powered_by_html: 'Powered by Discourse, best viewed with JavaScript enabled' log_in: "ログイン" purge_reason: "放棄されていたため自動的に削除、アカウントを停止しました。" disable_remote_images_download_reason: "ディスク容量が不足しているため、リモートでの画像ダウンロードは無効になっています。" @@ -192,7 +191,6 @@ ja: invalid: "は有効なカラーではありません" <<: *errors user_profile: - no_info_me: "
    プロフィールの自己紹介フィールドが空欄のようです。自己紹介をしてみませんか?
    " no_info_other: "
    %{name} は、まだプロフィールの自己紹介フィールドに何も書いていません
    " vip_category_name: "ラウンジ" vip_category_description: "トラストレベル3以上のメンバーのみが参加できるカテゴリ" @@ -901,7 +899,6 @@ ja: incorrect_username_email_or_password: "ユーザ名、メールアドレス、またはパスワードが違います" wait_approval: "サインアップありがとうござました。アカウントが承認され次第メールにて通知いたします。" active: "アカウントが利用可能になりました。" - activate_email: "

    あと少しです!アクティベーションメールを %{email} へ送信しました。メール内に記載されている指示に従い、認証を行って下さい。

    もし、届いていない場合は迷惑メールフォルダを確認するか、再度メールを送信してみてください。

    " not_activated: "まだログインできません。メールを送信済ですので、メールの指示に従ってあなたのアカウントを有効にしてください。" not_allowed_from_ip_address: "そのIPアドレスからは%{username} としてログインできません" admin_not_allowed_from_ip_address: "そのIPアドレスからは管理者としてログインできません" @@ -941,13 +938,6 @@ ja: subject_template: "%{invitee_name} あなたを %{site_domain_name}に招待しました。" invite_password_instructions: subject_template: "%{site_name} アカウントのパスワードを設定" - text_body_template: | - %{site_name} へようこそ - - このリンクをクリックしてパスワードを設定してください: - %{base_url}/users/password-reset/%{email_token} - - (リンクの有効期限が切れていた場合は、"パスワードを忘れたました"を選択しメールアドレスを入力してください) flag_reasons: off_topic: "あなたの投稿/トピックは「話題に関係ない」として通報されました" inappropriate: "あなたの投稿は「不適切」として通報されました。攻撃的、 [コミュニティガイドライン](/guidelines)に違反している可能性があります。" @@ -959,7 +949,6 @@ ja: disagreed: "伝えてくれてありがとうございます。調査しています" deferred: "伝えてくれてありがとうございます。調査しています" deferred_and_deleted: "伝えてくれてありがとうございます。投稿を削除しました" - temporarily_closed_due_to_flags: "このトピックへの通報が多いため、一時的に投稿が制限されています。" system_messages: welcome_user: subject_template: "%{site_name} へようこそ!" @@ -1032,60 +1021,8 @@ ja: click_here: "ここをクリック" mailing_list: new_topics: "新着トピック" - forgot_password: - text_body_template: | - [%{site_name}](%{base_url}) にて、パスワードのリセットリクエストが行われました。 - - もしリクエストを行っていない場合は、このメールを無視してください。 - - 新たなパスワードを設定する場合は次のリンクをクリックしてください: %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - [%{site_name}](%{base_url}) のあなたのアカウントに対してパスワード追加のリクエストがありました。 - あるいは、この検証済みメールアドレスに関連付けられている任意のサポートされているオンラインサービス(グーグル、フェイスブックなど)を使用してログインすることができます。 - - もし、リクエストをしていない場合は、このメールを無視してください。 - - パスワードを選択する場合は次のリンクをクリックしてください: - %{base_url}/users/password-reset/%{email_token} - account_created: - text_body_template: | - %{site_name}のあなたの新しいアカウントが作成されました。 - - 以下のリンクをクリックしてパスワードを設定してください: - %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - text_body_template: | - 以下のリンクをクリックして、%{site_name}で利用するメールアドレスを有効化してください: - - %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "%{site_name} への参加承認完了!" - text_body_template: | - %{site_name}へようこそ! - - スタッフが%{site_name}でのアカウントを承認しました。 - - アカウントを有効にするには以下のリンクをクリックしてください: - %{base_url}/users/activate-account/%{email_token} - - 上記のURLがクリック出来ない場合は、コピーし、ウェブブラウザのアドレスバーに貼り付けてください。 - - %{new_user_tips} - - [ガイドライン](%{base_url}/guidelines)をよく読み、しっかり守りましょう。 - - それでは、お楽しみください! - - (もし[スタッフメンバー](%{base_url}/about) に新規ユーザーとして連絡する必要がある場合、このメッセージに返信してください) - signup: - text_body_template: | - Welcome to %{site_name}! - - リンクをクリックして、アカウントを有効化してください: - %{base_url}/users/activate-account/%{email_token} - - もし、上記のURLがクリックできない場合は、ブラウザのアドレスバーへURLをコピーして貼り付けて下さい。 page_not_found: title: "ページが見つからないか、アクセス出来ない場所にあります。" popular_topics: "人気" diff --git a/config/locales/server.ko.yml b/config/locales/server.ko.yml index 0b6c769147..15edab89dc 100644 --- a/config/locales/server.ko.yml +++ b/config/locales/server.ko.yml @@ -26,7 +26,6 @@ ko: topics: "글타래" posts: "글" loading: "로딩중" - powered_by_html: 'Powered by Discourse, best viewed with JavaScript enabled' log_in: "로그인" disable_remote_images_download_reason: "디스크저장공간이 부족하여 원격 이미지 다운로드 기능이 비활성화 되었습니다." anonymous: "익명" @@ -208,7 +207,6 @@ ko: invalid: "유효한 색상이 아닙니다." <<: *errors user_profile: - no_info_me: "
    프로필이 현재 비어있습니다. 지금 작성하시겠습니까?
    " no_info_other: "
    %{name}님께서는 아직 프로필을 작성하지 않으셨습니다
    " vip_category_name: "라운지" vip_category_description: "회원등 3 혹은 그 이상의 회원만 입장가능한 카테고리" @@ -881,7 +879,6 @@ ko: incorrect_username_email_or_password: "계정 아이디, 이메일 또는 패스워드가 다릅니다." wait_approval: "가입해 주셔서 감사합니다. 곧 활성화 메일이 도착할 것 입니다." active: "당신의 계정이 활성화되었습니다." - activate_email: "

    거의 끝났습니다. 인증 이메일을 %{email} 주소로 보냈습니다. 설명을 따라서 인증을 마쳐주시면 됩니다.

    메일이 오지 않을 경우, 스팸폴더를 확인하거나 로그인 시도를 해서 다른 인증메일을 보내도록 할 수 있습니다..

    " not_activated: "당신은 아직 로그인 할 수 없습니다. 계정 활성화 이메일을 보냈습니다. 계정을 활성화하기 위해 지침을 따라주세요." not_allowed_from_ip_address: "이 IP 주소에서는 %{username} 으로 로그인 할 수 없습니다." admin_not_allowed_from_ip_address: "그 IP로는 관리자로 로그인할 수 없습니다." @@ -930,7 +927,6 @@ ko: disagreed: "알려줘서 감사합니다. 살펴보겠습니다." deferred: "알려줘서 감사합니다. 살펴보겠습니다." deferred_and_deleted: "알려줘서 감사합니다. 해당 글을 삭제했습니다." - temporarily_closed_due_to_flags: "이 글타래는 많은 신고가 들어와 임시로 닫힌 상태입니다." system_messages: welcome_user: subject_template: "%{site_name} 사이트에 오신것을 환영합니다!" @@ -1000,61 +996,8 @@ ko: digest: why: "%{last_seen_at}부터 지금까지 %{site_link} 사이트 근황" click_here: "클릭" - forgot_password: - text_body_template: | - [%{site_name}](%{base_url}) 사이트에서 누군가 당신의 패스워드를 재설정 요청하였습니다. - - 아래 링크를 클릭하여 패스워드를 재설정하세요: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - [%{site_name}](%{base_url})의 회원님 계정에 비밀번호를 추가 요청을 받았습니다. 이 이메일이 사용된 다른 온라인 서비스 (Google, Facebook, etc)를 이용해 로그인할 수 있습니다. - - 이 요청을 하지 않았다면, 이 메일은 무시해도 안전합니다. - - 비밀번호 선택 페이지: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - [%{site_name}](%{base_url})의 회원님 계정에 로그인 요청을 받았습니다. - - 이 요청을 하지 않았다면, 이 메일은 무시해도 안전합니다. - - 로그인 페이지: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - %{site_name}에 새 계정이 만들어 졌습니다. - - 비밀번호 설정 페이지: - %{base_url}/users/password-reset/%{email_token} signup_after_approval: subject_template: "당신은 %{site_name} 가입이 승인되었습니다!" - text_body_template: | - %{site_name}에 오신 것을 환영합니다! - - 스태프가 %{site_name} 계정을 승인했습니다. - - 새 계정을 활성화해서 시작하려면 아래를 클릭: - %{base_url}/users/activate-account/%{email_token} - - 링크가 안 될 경우 복사 붙여넣기로 주소에 넣어 들어가주세요. - - %{new_user_tips} - - 우리는 [문화시민의 커뮤니티 활동 지침](%{base_url}/guidelines)을 항상 따릅니다. - - 커뮤니티를 즐겨주세요! - - (새 사용자로서 할말이 있으시면 이글에 답변함으로써 [스태프](%{base_url}/about)들에게 이야기해주세요.) - signup: - text_body_template: | - %{site_name}에 오신것을 환영합니다! - - 사용자 계정 인증을 위해 아래 링크를 클릭해주세요: - %{base_url}/users/activate-account/%{email_token} - - 만약 위의 링크를 클릭 할 수 없으면 위 주소를 복사하여 웹브라우저에 붙여 넣으세요. page_not_found: popular_topics: "인기" recent_topics: "최근" diff --git a/config/locales/server.nb_NO.yml b/config/locales/server.nb_NO.yml index 0c69f108a9..75dee21328 100644 --- a/config/locales/server.nb_NO.yml +++ b/config/locales/server.nb_NO.yml @@ -27,7 +27,6 @@ nb_NO: topics: "Emner" posts: "innlegg" loading: "Laster" - powered_by_html: 'Drevet av Discourse, best nyttet med JavaScript påslått' log_in: "Logg inn" purge_reason: "Automatisk slettet som som forlatt, uaktivert konto" disable_remote_images_download_reason: "Nedlasting av bilder ble deaktivert grunnet mangel på tilgjengelig diskplass." @@ -265,16 +264,12 @@ nb_NO: attributes: hex: invalid: "er ikke en gyldig farge" - post_reply: - base: - different_topic: "Innlegg og svar må tilhøre samme kategori." web_hook: attributes: payload_url: invalid: "URL-adresse er ikke gyldig. Adressen skal starte med http:// eller https:// og ingen blanke tegn er tillatt." <<: *errors user_profile: - no_info_me: "
    Om meg-feltet på din profil er for øyeblikket blankt, ønsker du å fylle det ut?
    " no_info_other: "
    %{name} har ikke skrevet noe i Om meg-feltet på sin profil ennå
    " vip_category_name: "Salong" vip_category_description: "En kategori tilgjengelig for brukere på tillitsnivå 3 og høyere." @@ -813,7 +808,6 @@ nb_NO: incorrect_username_email_or_password: "Feil brukernavn, email eller passord" wait_approval: "Takk for at du registrerte deg. Vi vil varsle deg når din konto er blitt godkjent." active: "Kontoen din har blitt godkjent og er klar for bruk." - activate_email: "

    Nesten ferdig! Vi har sendt en e-post til %{email}. Følg lenken i e-posten for å aktivere kontoen din.

    Hvis den ikke dukker opp, sjekk søppepostmappen din eller logg inn på nytt for utsending av ny e-post.

    " not_available: "Ikke mulig. Prøv %{suggestion}?" something_already_taken: "Noe gikk galt, kanskje brukernavn eller email er allerede i bruk? Prøv 'glemt passord' linken." omniauth_error_unknown: "Noe gikk galt under innloggingsprosessen, prøv igjen." @@ -884,18 +878,6 @@ nb_NO: new_topics: "Nye emner" view_this_topic: "Vis dette emnet" back_to_top: "Tilbake til toppen" - account_created: - text_body_template: | - En ny konto ble registrert for deg på %{site_name} - - Trykk på følgende link for å velge et passord for kontoen din: - %{base_url}/users/password-reset/%{email_token} - signup: - text_body_template: | - Velkommen til %{site_name}! - - Trykk på denne lenken for å aktivere kontoen din: - %{base_url}/users/activate-account/%{email_token} page_not_found: title: "Oisann! Den siden finnes ikke eller er privat." popular_topics: "Populært" @@ -1063,11 +1045,25 @@ nb_NO: description: "Sikkerhetsmodus lar deg teste nettstedet ditt uten at tillegg eller tilpasninger lastes." wizard: step: + forum_title: + title: "Navn" contact: fields: contact_email: label: "E-post" placeholder: "navn@example.com" + contact_url: + label: "Nettside" + placeholder: "http://www.eksempel.no/kontakt-oss" + site_contact: + label: "Automatiserte meldinger" + corporate: + title: "Organisasjon" + fields: + company_short_name: + label: "Bedriftsnavn (kort)" + colors: + title: "Drakt" logos: title: "Logoer" fields: diff --git a/config/locales/server.nl.yml b/config/locales/server.nl.yml index cca826b804..9ae06602a2 100644 --- a/config/locales/server.nl.yml +++ b/config/locales/server.nl.yml @@ -27,12 +27,15 @@ nl: topics: "Topics" posts: "berichten" loading: "Laden" - powered_by_html: 'Mogelijk gemaakt door Discourse, best bekeken met JavaScript ingeschakeld' + powered_by_html: 'Mogelijk gemaakt door Discourse, best bekeken met JavaScript ingeschakeld' log_in: "Aanmelden" purge_reason: "Automatisch verwijderd als verlaten, gedeactiveerde account." disable_remote_images_download_reason: "Het downloaden van externe afbeeldingen is uitgeschakeld, omdat er niet genoeg schijfruimte beschikbaar was." anonymous: "Anoniem" remove_posts_deleted_by_author: "Verwijderd door gebruiker" + themes: + bad_color_scheme: "Kan thema niet bijwerken, ongeldig kleurenschema" + other_error: "Er is iets misgegaan bij het bijwerken van het thema" emails: incoming: default_subject: "Dit topic heeft een titel nodig" @@ -110,7 +113,7 @@ nl: s3_upload_bucket_is_required: "U kunt geen uploads naar S3 toestaan voordat u de 's3_upload_bucket' hebt opgegeven." invite: not_found: "Uw uitnodigingstoken is ongeldig. Neem contact op met de beheerder van de website." - user_exists: "%{email} hoeft niet te worden uitgenodigd; hij of zij heeft al een account!" + user_exists: "%{email} hoeft niet te worden uitgenodigd; hij of zij heeft al een account!" bulk_invite: file_should_be_csv: "Het geüploade bestand dient de CSV-indeling te hebben." error: "Er is een fout opgetreden tijdens het uploaden van dat bestand. Probeer het later opnieuw." @@ -280,7 +283,7 @@ nl: Hebt u overwogen *andere* mensen in de discussie ook te beantwoorden? Een goede discussie omvat veel stemmen en perspectieven. - Als u uw conversatie met deze bepaalde gebruiker uitvoerig wilt voortzetten, [stuur deze dan een persoonlijk bericht](/users/%{reply_username}). + Als u uw discussie met deze bepaalde gebruiker uitvoerig wilt voortzetten, [stuur deze dan een persoonlijk bericht](/u/%{reply_username}). too_many_replies: | ### U hebt de antwoordlimiet voor dit topic bereikt @@ -337,9 +340,17 @@ nl: attributes: payload_url: invalid: "URL is ongeldig. URL's moeten http:// of https:// bevatten, en spaties zijn niet toegestaan." + custom_emoji: + attributes: + name: + taken: wordt al door een andere emoji gebruikt + topic_status_update: + attributes: + execute_at: + in_the_past: "moet in de toekomst liggen." <<: *errors user_profile: - no_info_me: "
    het veld Over mij van uw profiel is momenteel leeg, zou u dit willen invullen?
    " + no_info_me: "
    het veld Over mij van uw profiel is momenteel leeg; zou u dit willen invullen?
    " no_info_other: "
    %{name} heeft nog niets in het veld Over mij van zijn of haar profiel ingevoerd
    " vip_category_name: "Lounge" vip_category_description: "Een exclusieve categorie voor leden met vertrouwensniveau 3 en hoger." @@ -544,6 +555,12 @@ nl: approval_required: "Voordat u toegang hebt tot dit forum, moet een moderator uw nieuwe account handmatig goedkeuren. U ontvangt een e-mail zodra uw account is goedgekeurd!" missing_session: "We kunnen niet vaststellen of uw account is aangemaakt; zorg ervoor dat u cookies hebt ingeschakeld." activated: "Sorry, deze account is al geactiveerd." + admin_confirm: + title: "Beheerdersaccount bevestigen" + description: "Weet u zeker dat u %{target_username} een beheerder wilt laten worden?" + grant: "Beheerderstoegang verlenen" + complete: "%{target_username} is nu een beheerder." + back_to: "Terug naar %{title}" post_action_types: off_topic: title: 'Off-topic' @@ -557,7 +574,7 @@ nl: email_body: "%{link}\n\n%{message}" inappropriate: title: 'Ongepast' - description: 'Dit bericht bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze richtlijnen zou beschouwen.' + description: 'Dit bericht bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze gemeenschapsrichtlijnen zou beschouwen.' long_form: 'heeft dit als ongepast gemarkeerd' notify_user: title: '@{{username}} een bericht sturen' @@ -597,7 +614,7 @@ nl: long_form: 'heeft dit als spam gemarkeerd' inappropriate: title: 'Ongepast' - description: 'Dit topic bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze richtlijnen zou beschouwen.' + description: 'Dit topic bevat inhoud die een redelijk persoon als beledigend, kwetsend of een overtreding van onze gemeenschapsrichtlijnen zou beschouwen.' long_form: 'heeft dit als ongepast gemarkeerd' notify_moderators: title: "Iets anders" @@ -797,24 +814,39 @@ nl: facebook_config_warning: 'De server is geconfigureerd om registratie en aanmelding via Facebook toe te staan (enable_facebook_logins), maar de waarden voor app-ID en app-geheim zijn niet ingesteld. Ga naar de Website-instellingen en werk de instellingen bij. Bekijk deze handleiding voor meer info.' twitter_config_warning: 'De server is geconfigureerd om registratie en aanmelding via Twitter toe te staan (enable_twitter_logins), maar de waarden voor sleutel en geheim zijn niet ingesteld. Ga naar de Website-instellingen en werk de instellingen bij. Bekijk deze handleiding voor meer info.' github_config_warning: 'De server is geconfigureerd om registratie en aanmelding via GitHub toe te staan (enable_github_logins), maar de waarden voor client-ID en -geheim zijn niet ingesteld. Ga naar de Website-instellingen en werk de instellingen bij. Bekijk deze handleiding voor meer info.' + s3_config_warning: 'De server is geconfigureerd om bestanden naar s3 te uploaden, maar minstens één van de volgende waarden is niet ingesteld: s3_access_key_id, s3_secret_access_key, s3_use_iam_profile, of s3_upload_bucket. Ga naar de Website-instellingen en werk de instellingen bij. Zie ''How to set up image uploads to S3?'' voor meer info.' + s3_backup_config_warning: 'De server is geconfigureerd om back-ups naar s3 te uploaden, maar minstens één van de volgende waarden is niet ingesteld: s3_access_key_id, s3_secret_access_key, s3_use_iam_profile, of s3_backup_bucket. Ga naar de Website-instellingen en werk de instellingen bij. Zie ''How to set up image uploads to S3?'' voor meer info.' image_magick_warning: 'De server is geconfigureerd om miniaturen van grote afbeeldingen te maken, maar ImageMagick is niet geïnstalleerd. Installeer ImageMagick met uw favoriete pakketbeheerder, of download de nieuwste versie.' - failing_emails_warning: 'Er zijn %{num_failed_jobs} e-mail jobs mislukt. Controleer in app.yml of de e-mailserver instelling correct zijn. Toon de mislukte jobs in Sidekiq.' - subfolder_ends_in_slash: "Je submap setup is onjuist; de DISCOURSE_RELATIVE_URL_ROOT eindigt in een schuine streep." + failing_emails_warning: 'Er zijn %{num_failed_jobs} mislukte e-mailtaken. Controleer uw bestand app.yml en zorg ervoor dat de mailserverinstellingen juist zijn. Bekijk de mislukte taken in Sidekiq.' + subfolder_ends_in_slash: "Uw submapconfiguratie is onjuist; de DISCOURSE_RELATIVE_URL_ROOT eindigt met een schuine streep." + email_polling_errored_recently: + one: "E-mailpolling heeft de afgelopen 24 uur een fout gegenereerd. Bekijk de logboeken voor meer details." + other: "E-mailpolling heeft de afgelopen 24 uur %{count} fouten gegenereerd. Bekijk de logboeken voor meer details." + missing_mailgun_api_key: "De server is geconfigureerd om e-mails via Mailgun te verzenden, maar u hebt geen API-sleutel opgegeven voor verificatie van de webhookberichten." + bad_favicon_url: "De favicon wordt niet geladen. Controleer uw favicon_url-instelling in de Website-instellingen." + poll_pop3_timeout: "Time-out voor verbinding met de POP3-server. Binnenkomende e-mail kon niet worden opgehaald. Controleer uw POP3-instellingen en serviceprovider." + poll_pop3_auth_error: "Verbinding met de POP3-server is mislukt met een authenticatiefout. Controleer uw POP3-instellingen." site_settings: - censored_words: "Woorden welke automatisch vervangen zullen worden met ■■■■" - delete_old_hidden_posts: "Auto-delete alle verborgen berichten die meer dan 30 dagen verborgen zijn." - default_locale: "De standaard taal van dit Discourse forum (ISO 639-1 code)" - allow_user_locale: "Gebruikers kunnen zelf een taal instellen voor de interface van de site" - min_post_length: "Minimum lengte van een bericht in tekens" - min_first_post_length: "Minimale toegestane lengte voor het eerste bericht (topic body) in tekens" - min_private_message_post_length: "Minimale toegestane lengte in tekens voor berichten" + censored_words: "Woorden die automatisch door ■■■■ zullen worden vervangen" + censored_pattern: "Regex-patroon dat automatisch door ■■■■ zal worden vervangen" + delete_old_hidden_posts: "Verborgen berichten die meer dan 30 dagen verborgen blijven automatisch verwijderen" + default_locale: "De standaardtaal van dit Discourse-forum (ISO 639-1-code)" + allow_user_locale: "Gebruikers toestaan om een eigen voorkeur voor de interfacetaal te kiezen" + set_locale_from_accept_language_header: "Taal van interface voor anonieme gebruikers instellen op basis van de taalheaders van hun webbrowser. (EXPERIMENTEEL, werkt niet met anonieme cache)" + min_post_length: "Minimaal toegestane lengte van een bericht in tekens" + min_first_post_length: "Minimaal toegestane lengte van eerste bericht (topictekst) in tekens" + min_private_message_post_length: "Minimaal toegestane berichtlengte in tekens voor privéberichten" max_post_length: "Maximaal toegestane lengte van een bericht in tekens" - min_topic_title_length: "Minimum lengte van een topictitel" + topic_featured_link_enabled: "Plaatsing van een koppeling met topics toestaan" + show_topic_featured_link_in_digest: "Koppeling naar aanbevolen topics in de samenvattingsmail tonen" + min_topic_title_length: "Minimaal toegestane lengte van een topictitel in tekens" max_topic_title_length: "Maximaal toegestane lengte van een topictitel in tekens" - min_private_message_title_length: "Minimale toegestane titel lengte in tekens voor berichten" - min_search_term_length: "Minimum lengte van een zoekterm in tekens" + min_private_message_title_length: "Minimaal toegestane titellengte voor een bericht in tekens" + min_search_term_length: "Minimumlengte van een zoekterm in tekens" + search_prefer_recent_posts: "Als doorzoeken van uw grote forum traag werkt, probeert deze optie eerst een index van recentere berichten" + search_recent_posts_size: "Het aantal in de index te behouden recente berichten " allow_uncategorized_topics: "Topics zonder categorie toestaan. WAARSCHUWING: Alle topics moet weer aan een categorie zijn toegekend voor dit wordt uitgezet." - allow_duplicate_topic_titles: "Sta topics met dezelfde titels toe" + allow_duplicate_topic_titles: "Topics met dezelfde identieke titels toestaan" unique_posts_mins: "Hoeveel minuten iemand moet wachten voordat deze een bericht met dezelfde inhoud mag plaatsen" educate_until_posts: "Laat de popup voor nieuwe gebruikers zien als een gebruiker start met het typen van hun eerste (n) nieuwe berichten in de berichtopsteller." title: "De naam van deze site, wordt gebruikt in de titeltag" @@ -825,6 +857,7 @@ nl: crawl_images: "Haal afbeeldingen van externe URLs om de correcte breedte- en hoogtedimensies in te voegen" download_remote_images_to_local: "Download externe afbeeldingen en sla ze lokaal op. Dit voorkomt dat afbeeldingen niet meer beschikbaar zouden kunnen worden." download_remote_images_threshold: "Minimum schijfruimte vereist om externe afbeeldingen lokaal te downloaden (percentage)" + download_remote_images_max_days_old: "Geen externe afbeeldingen downloaden voor berichten die meer dan n dagen oud zijn." disabled_image_download_domains: "Externe afbeeldingen zullen nooit gedownload worden van deze domeinen. Pipe-gescheiden lijst." editing_grace_period: "De eerste (n) seconden na het plaatsen van een bericht leidt het wijzigen ervan niet tot een nieuwe versie in de geschiedenis van het bericht." post_edit_time_limit: "Berichten mogen na (n) minuten nog gewijzigd of verwijderd worden door de schrijver. Gebruik 0 voor onbeperkt." @@ -838,19 +871,21 @@ nl: exclude_rel_nofollow_domains: "Een lijst van domeinen waar ´nofollow´ niet aan de links moet worden toegevoegd. (voorbeelddomein.com staat automatisch sub.voorbeelddomein toe). Voeg minimaal het 'top-level' domein van deze site toe om web crawlers alle content te laten vinden. Als delen van deze site zich op andere domeinen bevinden, voeg deze eveneens toe." post_excerpt_maxlength: "Maximale lengte van een uittreksel / samenvatting van een bericht." post_onebox_maxlength: "Maximale lengte van een 'oneboxed Discourse' bericht in tekens." + max_oneboxes_per_post: "Maximale aantal Oneboxes in een bericht." logo_url: "De logo afbeelding links bovenaan je site moet de vorm hebben van een brede rechthoek. Indien leeg wordt de site titeltekst getoond." - logo_small_url: "De kleine logo afbeelding links boven aan je site moet de vorm van een vierkant hebben en wordt getoond als naar beneden wordt gescrold. Indien leeg wordt een homepage symbool getoond." + logo_small_url: "De kleine logoafbeelding linksboven op uw website dient een vierkante vorm te hebben en wordt getoond als omlaag wordt gescrold. Bij leeg laten wordt een startpaginasymbool getoond." favicon_url: "Een favicon voor je site, zie http://nl.wikipedia.org/wiki/Favicon. Voor een correcte werking via CDN moet het een png zijn." - mobile_logo_url: "De logo afbeelding met vaste positie voor linksboven op je mobiele site. De afbeelding moet vierkant zijn. Indien leeg wordt `logo_url` gebruikt. Bijv.: http://example.com/uploads/default/logo.png" + mobile_logo_url: "De logoafbeelding met vaste positie voor linksboven op uw mobiele website. De afbeelding dient een vierkante vorm te hebben. Bij leeg laten wordt 'logo_url' gebruikt. Vb.: http://example.com/uploads/default/logo.png" apple_touch_icon_url: "Pictogram voor Apple touch-apparaten. Aanbevolen grootte is 144px bij 144px." notification_email: "Het afzender email adres dat wordt gebruikt voor het verzenden van alle essentiële systeem emails. Voor het domein dat hier gespecificeerd is moeten de SPF, DKIM en reverse PTR records goed ingesteld zijn om de mails te doen aankomen." email_custom_headers: "Een lijst van custom email headers, gescheiden door een pipe (|)" - email_subject: "Wijzigbaar onderwerp formaat voor standaard emails. Bekijk https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801 voor meer informatie" - summary_score_threshold: "De minimum score voor een bericht om opgenomen te worden in 'Samenvatting Topic' " - summary_posts_required: "Minimum aantal berichten in een topic voor weergave ´Samenvatting Topic´" - summary_likes_required: "Minimum aantal Likes in een topic voor weergave ´Samenvatting Topic´" - summary_percent_filter: "Als iemand op 'Samenvatting Topic' klikt, laat dan de top % van de berichten zien" - summary_max_results: "Maximaal aantal berichten in weergave ´Samenvatting Topic´" + email_subject: "Aanpasbare onderwerpnotatie voor standaard-e-mails. Bekijk https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801 voor meer informatie" + summary_score_threshold: "De minimaal vereiste score voor een bericht om in 'Dit topic samenvatten' te worden opgenomen" + summary_posts_required: "Minimale aantal berichten in een topic voordat 'Dit topic samenvatten' wordt ingeschakeld" + summary_likes_required: "Minimale aantal likes in een topic voordat 'Dit topic samenvatten' wordt ingeschakeld" + summary_percent_filter: "Wanneer een gebruiker op 'Dit topic samenvatten' klikt, de top % van berichten tonen" + summary_max_results: "Maximale aantal getoonde berichten in 'Dit topic samenvatten'" + enable_private_messages: "Gebruikers met vertrouwensniveau 1 (instelbaar via het minimale niveau om berichten te verzenden) toestaan om berichten aan te maken en op berichten te antwoorden. Houd er rekening mee dat stafleden altijd berichten kunnen verzenden." enable_long_polling: "De 'message bus' die gebruikt wordt voor notificaties kan 'long polling' gebruiken." long_polling_base_url: "Basis URL voor ´long polling´ (indien via een CDN dynamische content wordt toegepast, kies hier dan 'origin pull´) b.v. http://originele.website.com" long_polling_interval: "De hoeveelheid tijd die de server zou moeten wachten voordat het clients beantwoord als er geen data te verzenden is (alleen voor ingelogde gebruikers)" @@ -858,6 +893,7 @@ nl: anon_polling_interval: "Hoe vaak een anonieme client een 'poll' moet uitvoeren in milliseconden" background_polling_interval: "Hoe vaak zouden de clients moeten pollen in milliseconden (als het venster in de achtergrond staat)" cooldown_minutes_after_hiding_posts: "Het aantal minuten dat een gebruiker moet wachten voordat zij een door de gemeenschap verborgen bericht kunnen wijzigen" + max_topics_in_first_day: "Het maximale aantal topics dat een gebruiker in de 24 uursperiode na het schrijven van een eerste bericht mag aanmaken" tl2_additional_likes_per_day_multiplier: "Verhoog de limiet van likes per dag voor tl2 (lid) door te vermenigvuldigen met dit getal" tl3_additional_likes_per_day_multiplier: "Verhoog de limiet van likes per dag voor tl3 (vaste bezoeker) door te vermenigvuldigen met dit getal" tl4_additional_likes_per_day_multiplier: "Verhoog de limiet van likes per dag voor tl4 (leider) door te vermenigvuldigen met dit getal" @@ -919,8 +955,8 @@ nl: enable_signup_cta: "Toon een notificatie aan terugkerende anonieme gebruikers waarin gevraagd wordt zich aan te melden voor een account." enable_yahoo_logins: "Yahoo-authenticatie inschakelen" enable_google_oauth2_logins: "Google Oauth2 authenticatie toestaan. Dit is de authenticatiemethode die momenteel door Google wordt ondersteund. Vereist key en secret." - google_oauth2_client_id: "Client ID van je Google applicatie." - google_oauth2_client_secret: "Client secret van je Google applicatie." + google_oauth2_client_id: "Client-ID van uw Google-toepassing." + google_oauth2_client_secret: "Client-geheim van uw Google-toepassing." enable_twitter_logins: "Twitter-authenticatie inschakelen; vereist twitter_consumer_key en twitter_consumer_secret." twitter_consumer_key: "consumer_key (registreer op dev.twitter.com)" twitter_consumer_secret: "consumer secret (registreer op dev.twitter.com)" @@ -931,6 +967,7 @@ nl: enable_github_logins: "Github-authenticatie inschakelen; vereist github_client_id en github_client_secret." github_client_id: "github_client_id (registreer op https://github.com/settings/applications)" github_client_secret: "github_client_secret (registreer op https://github.com/settings/applications)" + readonly_mode_during_backup: "Alleen-lezenmodus tijdens het maken van een back-up inschakelen" allow_restore: "Herstellen van data toestaan, waarbij ALLE site-data wordt overschreven! Laat op 'false' staan, tenzij je een back-up terug wil zetten." maximum_backups: "Het maximale aantal back-ups om op schijf te bewaren. Oudere back-ups worden automatisch verwijderd." automatic_backups_enabled: "Automatische back-ups uitvoeren volgens de opgegeven back-up frequentie." @@ -1009,7 +1046,7 @@ nl: title_min_entropy: "De minimale entropie (unieke tekens, niet-Engels telt zwaarder) die voor een topictitel is vereist." body_min_entropy: "De minimale entropie (unieke tekens, niet-Engels telt zwaarder) die voor de hoofdtekst van een bericht is vereist." allow_uppercase_posts: "Sta alleen hoofdletters toe in een topic titel of bericht inhoud." - title_fancy_entities: "Converteer algemene ASCII-tekens naar fancy HTML in topictitels, zoals SmartyPants (http://daringfireball.net/projects/smartypants)" + title_fancy_entities: "Algemene ASCII-tekens naar decoratieve HTML-entiteiten converteren in topictitels, zoals SmartyPants (http://daringfireball.net/projects/smartypants/)" min_title_similar_length: "De minimale lengte die een titel van een topic moet hebben voordat er wordt gezocht naar vergelijkbare topics." min_body_similar_length: "De minimale lengte die de inhoud van een bericht moet hebben voordat er wordt gezocht naar vergelijkbare topics." category_colors: "Een lijst me hexadecimale kleurwaardes die gebruikt kunnen worden voor categorieën." @@ -1018,6 +1055,7 @@ nl: max_attachment_size_kb: "De maximale grootte van te uploaden bijlages in kB. Dit moet ook worden geconfigureerd in nginx (client_max_body_size) / apache of proxy." authorized_extensions: "Een lijst met bestandsextensies die mogen worden geupload (gebruik '*' voor alle bestandsextensies)" max_similar_results: "Hoeveel vergelijkbare topics om te laten zien boven de editor bij het opstellen van een nieuw topic. Vergelijking is gebaseerd op titel en inhoud." + max_image_megapixels: "Maximale aantal toegestane megapixels voor een afbeelding." title_prettify: "Voorkom veel voorkomende fouten in titels (alles in hoofdletters, eerste woord zonder hoofdletter, meerdere ! en ?, een extra . aan het eind, etc." topic_views_heat_low: "Na dit aantal keer bekeken zal het aantal keer bekeken veld licht gemarkeerd zijn." topic_views_heat_medium: "Na dit aantal keer bekeken zal het aantal keer bekeken veld middelmatig gemarkeerd zijn." @@ -1056,6 +1094,7 @@ nl: strip_images_from_short_emails: "Verwijder afbeeldingen met grootte van minder dan 2800 Bytes uit e-mails." short_email_length: "Korte e-mail lengte in Bytes" display_name_on_email_from: "Toon de volledige naam in het afzenderveld van e-mails." + max_emails_per_day_per_user: "Maximale aantal e-mails dat per dag naar gebruikers wordt verzonden. 0 om de limiet uit te schakelen" enable_staged_users: "Maak automatisch staged gebruikers aan bij het verwerken van inkomende e-mails." maximum_staged_users_per_email: "Maximale aantal staged gebruikers dat bij het verwerken van een inkomende e-mail wordt aangemaakt." pop3_polling_enabled: "Poll via POP3 voor antwoorden per e-mail." @@ -1068,12 +1107,12 @@ nl: log_mail_processing_failures: "Log alle e-mail verwerkingsfouten naar http://yoursitename.com/logs" email_in: "Sta toe dat gebruikers een nieuw topic via e-mail (vereist het pollen van pop3). Configureer het adres in de \"Instellingen\" tabblad van elke categorie." email_in_min_trust: "Het minium trustlevel dat een gebruiker moet hebben om nieuwe topics te kunnen mailen." - email_prefix: "Het [label] gebruikt in het onderwerp van e-mails. Als standaard wordt 'titel' gebruikt als niet gezet." + email_prefix: "Het [label] dat in het onderwerp van e-mails wordt gebruikt. Als niets is ingevuld, wordt 'title' gebruikt." email_site_title: "De titel van de website die als de afzender van e-mails van de website wordt gebruikt. Standaard wordt 'titel' gebruikt als niets is ingesteld. Gebruik deze instelling als uw 'titel' tekens bevat die niet in tekenreeksen van e-mailafzenders zijn toegestaan." minimum_topics_similar: "Hoeveel topics moeten er aanwezig zijn voordat er vergelijkbare topics worden voorgesteld bij het opstellen van nieuwe topics." relative_date_duration: "Na hoeveel dagen moet een post datum niet meer worden getoond als relatieve datum (7d) maar als absolute datum (20 feb)." delete_user_max_post_age: "Gebruikers wiens eerste bericht ouder dan (x) dagen is kunnen niet meer verwijderd worden." - delete_all_posts_max: "Het maximaal aantal berichten dat ineens verwijderd kan worden met de 'Verwijder alle berichten'-knop. Als een gebruiker meer berichten heeft, kunnen de berichten niet in een keer verwijderd worden en kan de gebruiker dus niet verwijderd worden." + delete_all_posts_max: "Het maximale aantal berichten dat tegelijk met de knop 'Alle berichten verwijderen' kan worden verwijderd. Als een gebruiker meer berichten heeft, kunnen de berichten niet allemaal tegelijk worden verwijderd, en kan de gebruiker niet worden verwijderd." username_change_period: "The number of days after registration that accounts can change their username (0 om wijziging niet toe te staan)." email_editable: "Gebruikers mogen hun e-mailadres na registratie nog wijzigen." allow_uploaded_avatars: "Sta gebruikers toe eigen profielfoto's te uploaden." @@ -1081,8 +1120,12 @@ nl: allow_animated_thumbnails: "Genereer bewegende miniaturen van bewegende gifs." default_avatars: "URL's naar standaard te gebruiken avatars voor nieuwe gebruikers, totdat ze deze veranderen." automatically_download_gravatars: "Download Gravatars voor gebruikers bij account creatie of aanpassing van email." + digest_topics: "Het maximale aantal populaire topics dat in de e-mailsamenvatting wordt weergegeven." + digest_posts: "Het maximale aantal populaire berichten dat in de e-mailsamenvatting wordt weergegeven." + digest_other_topics: "Het maximale aantal topics dat in de sectie 'Nieuw in topics en categorieën die u volgt' van de e-mailsamenvatting wordt getoond." + email_link_color: "De kleur van koppelingen in HTML-e-mails. Voer een kleurnaam ('blue') of hex-waarde ('#0000FF') in." detect_custom_avatars: "Wel of niet te verifiëren of gebruikers eigen profielfoto's hebben geüpload. " - max_daily_gravatar_crawls: "Maximaal aantal keren op een dag dat Discourse Gravatar zal controleren voor aangepaste avatars" + max_daily_gravatar_crawls: "Maximale aantal keren dat Discourse op een dag bij Gravatar op aangepaste gravatars controleert" public_user_custom_fields: "Een toegestane lijst van veldnamen voor een gebruiker dat publiekelijk kan worden getoond." staff_user_custom_fields: "Een toegestane lijst van veldnamen voor een gebruiker dat aan medewerkers wordt getoond." enable_user_directory: "Biedt een overzicht van gebruikers aan om door te bladeren." @@ -1092,6 +1135,7 @@ nl: hide_user_profiles_from_public: "Uitschakelen gebruikerskaarten, gebruikersprofielen en het gebruikersoverzicht voor anonieme gebruikers." allow_profile_backgrounds: "Gebruikers mogen een profielachtergrond instellen." sequential_replies_threshold: "Aantal berichten dat een gebruiker achter elkaar in één topic moet plaatsen voordat hij een melding krijgt over te veel opeenvolgende reacties." + get_a_room_threshold: "Aantal berichten dat een gebruiker aan dezelfde persoon in hetzelfde topic moet richten voordat deze wordt gewaarschuwd." enable_mobile_theme: "Mobiele apparaten gebruiken een mobiel-vriendelijke theme met de mogelijkheid te schakelen naar de volledige site. Schakel deze optie uit als je een eigen stylesheet wil gebruiken die volledig responsive is." dominating_topic_minimum_percent: "Welk percentage van de berichten een gebruiker moet maken in een onderwerp voordat ze worden herinnerd aan het te veel domineren van een topic." suppress_uncategorized_badge: "Laat de badge niet zien voor topics zonder categorie in de topiclijsten." @@ -1099,6 +1143,7 @@ nl: disable_edit_notifications: "Schakelt bewerkingsnotificaties van de systeemgebruiker uit als 'download_remote_images_to_local' actief is." automatically_unpin_topics: "Automatisch ont-pinnen van topics als de gebruiker onderaan de pagina komt." topic_page_title_includes_category: "Paginatitel van topic bevat de categorienaam." + share_anonymized_statistics: "Geanonimiseerde gebruiksstatistieken delen." full_name_required: "Volledige naam is een verplicht veld van het gebruikersprofiel." enable_names: "Toon de volledige naam van de gebruiker op hun profiel, gebruikerskaart en e-mails. Scahkel uit om volledige naam overal te verbergen." display_name_on_posts: "Laat de volledige naam van een gebruiker bij zijn berichten zien, na de @gebruikersnaam" @@ -1142,6 +1187,14 @@ nl: default_categories_watching: "Lijst van categorieën die standaard worden bekeken." default_categories_tracking: "Lijst van categorieën die standaard worden bijgehouden." default_categories_muted: "Lijst van categorieën die standaard worden gedempt." + max_user_api_reqs_per_day: "Maximale aantal API-aanvragen van gebruiker per sleutel per dag" + max_user_api_reqs_per_minute: "Maximale aantal API-aanvragen van gebruiker per sleutel per minuut" + allow_user_api_keys: "Genereren van API-sleutels van gebruiker toestaan" + allow_user_api_key_scopes: "Lijst van toegestane scopes voor API-sleutels van gebruiker" + max_api_keys_per_user: "Maximale aantal API-aanvragen van gebruiker per gebruiker" + min_trust_level_for_user_api_key: "Vereiste vertrouwensniveau voor genereren van API-sleutels van gebruiker" + allowed_user_api_auth_redirects: "Toegestane URL voor authenticatie-omleiding voor API-sleutels van gebruiker" + allowed_user_api_push_urls: "Toegestane URL's voor server-push naar gebruikers-API" errors: invalid_email: "Ongeldig e-mailadres." invalid_username: "Er is geen gebruiker met die gebruikersnaam." @@ -1156,12 +1209,21 @@ nl: invalid_string_min: "Moet te minste uit %{min} tekens bestaan." invalid_string_max: "Mag uit niet meer dan %{max} tekens bestaan." invalid_reply_by_email_address: "Waarde moet '%{reply_key}' bevatten en moet afwijken van de e-mail notificatie." + invalid_regex: "Regex is ongeldig of niet toegestaan." search: within_post: "#%{post_number} door %{username}" types: category: 'Categorieën' topic: 'Resultaten' user: 'Leden' + sso: + login_error: "Aanmeldingsfout" + not_found: "Uw account kon niet worden gevonden. Neem contact op met de beheerder van de website." + account_not_approved: "Uw account wacht op goedkeuring. U ontvangt een e-mailmelding zodra deze is goedgekeurd." + unknown_error: "Er is een probleem met uw account. Neem contact op met de beheerder van de website." + timeout_expired: "Time-out bij accountaanmelding, probeer u opnieuw aan te melden." + no_email: "Er is geen e-mailadres opgegeven. Neem contact op met de beheerder van de website." + email_error: "Er kon geen account met het e-mailadres %{email} worden geregistreerd. Neem contact op met de beheerder van de website." original_poster: "Oorspronkelijk geplaatst door" most_posts: "Meer berichten" most_recent_poster: "Meest recente schrijver" @@ -1180,42 +1242,65 @@ nl: post_revision_text: "Eigendom overgezet van %{old_user} naar %{new_user}" deleted_user: "een verwijderde gebruiker" topic_statuses: - archived_enabled: "Deze topic is nu gearchiveerd en kan niet meer veranderd worden." - archived_disabled: "Deze topic is niet langer gearchiveerd en kan weer veranderd worden." - closed_enabled: "Deze topic is nu gesloten. Nieuwe reacties worden niet langer geaccepteerd." - closed_disabled: "Deze topic is nu geopend. Nieuwe reacties worden weer geaccepteerd." + archived_enabled: "Dit topic is nu gearchiveerd. Het is bevroren en kan op geen enkele manier worden gewijzigd." + archived_disabled: "Dit topic is nu niet meer gearchiveerd. Het is niet meer bevroren en kan worden gewijzigd." + closed_enabled: "Dit topic is nu gesloten. Nieuwe antwoorden zijn niet meer toegestaan." + closed_disabled: "Dit topic is nu geopend. Nieuwe antwoorden zijn toegestaan." + autoclosed_message_max_posts: + one: "Dit bericht is na het bereiken van de maximumlimiet van 1 antwoord automatisch gesloten." + other: "Dit bericht is na het bereiken van de maximumlimiet van %{count} antwoorden automatisch gesloten." + autoclosed_topic_max_posts: + one: "Dit topic is na het bereiken van de maximumlimiet van 1 antwoord automatisch gesloten." + other: "Dit topic is na het bereiken van de maximumlimiet van %{count} antwoorden automatisch gesloten." autoclosed_enabled_days: - one: "Dit topic is automatisch na één dag gesloten. Reageren is niet meer mogelijk." - other: "Dit topic is automatisch na %{count} dagen gesloten. Reageren is niet meer mogelijk." + one: "Dit topic is na 1 dag automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." + other: "Dit topic is na %{count} dagen automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." autoclosed_enabled_hours: one: "Dit topic is na 1 uur automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." other: "Dit topic is na %{count} uur automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." autoclosed_enabled_minutes: - one: "Dit topic is automatisch na één minuut gesloten. Reageren is niet meer mogelijk." - other: "Dit topic is automatisch na %{count} minuten gesloten. Reageren is niet meer mogelijk." + one: "Dit topic is na 1 minuut automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." + other: "Dit topic is na %{count} minuten automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." autoclosed_enabled_lastpost_days: - one: "Dit topic is automatisch gesloten één dag na het laatste antwoord. Reageren is niet meer mogelijk." - other: "Dit topic is automatisch gesloten %{count} dagen na het laatste antwoord. Reageren is niet meer mogelijk." + one: "Dit topic is 1 dag na het laatste antwoord automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." + other: "Dit topic is %{count} dagen na het laatste antwoord automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." autoclosed_enabled_lastpost_hours: one: "Dit topic is 1 uur na het laatste antwoord automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." other: "Dit topic is %{count} uur na het laatste antwoord automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." autoclosed_enabled_lastpost_minutes: - one: "Dit topic is automatisch gesloten één minuut na het laatste antwoord. Reageren is niet meer mogelijk." - other: "Dit topic is automatisch gesloten %{count} minuten na het laatste antwoord. Reageren is niet meer mogelijk." - autoclosed_disabled: "Deze topic is geopend. Reageren is mogelijk." + one: "Dit topic is 1 minuut na het laatste antwoord automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." + other: "Dit topic is %{count} minuten na het laatste antwoord automatisch gesloten. Nieuwe antwoorden zijn niet meer toegestaan." + autoclosed_disabled_days: + one: "Dit topic is na 1 dag automatisch geopend." + other: "Dit topic is na %{count} dagen automatisch geopend." + autoclosed_disabled_hours: + one: "Dit topic is na 1 uur automatisch geopend." + other: "Dit topic is na %{count} uur automatisch geopend." + autoclosed_disabled_minutes: + one: "Dit topic is na 1 minuut automatisch geopend." + other: "Dit topic is na %{count} minuten automatisch geopend." + autoclosed_disabled_lastpost_days: + one: "Dit topic is 1 dag na het laatste antwoord automatisch geopend." + other: "Dit topic is %{count} dagen na het laatste antwoord automatisch geopend." + autoclosed_disabled_lastpost_hours: + one: "Dit topic is 1 uur na het laatste antwoord automatisch geopend." + other: "Dit topic is %{count} uur na het laatste antwoord automatisch geopend." + autoclosed_disabled_lastpost_minutes: + one: "Dit topic is 1 minuut na het laatste antwoord automatisch geopend." + other: "Dit topic is %{count} minuten na het laatste antwoord automatisch geopend." + autoclosed_disabled: "Dit topic is nu geopend. Nieuwe antwoorden zijn toegestaan." autoclosed_disabled_lastpost: "Dit topic is nu geopend. Nieuwe reacties worden weer geaccepteerd." - pinned_enabled: "Deze topic is nu vastgepind en zal bovenaan de categorie verschijnen totdat de pin door de staf voor iedereen of een gebruiker voor zichzelf verwijderd wordt." - pinned_disabled: "Deze topic is niet langer gepind en zal niet meer bovenaan de lijst van topics in zijn categorie staan." - pinned_globally_enabled: "Deze topic is nu globaal vastgepind en zal bovenaan de lijst van topics in zijn categorie en alle andere topiclijsten staan. Wanneer iemand op 'Verwijder pin' drukt, zal de topic voor diegene niet meer bovenaan staan." - pinned_globally_disabled: "Deze topic is niet langer gepind en zal niet meer bovenaan de lijst van topics in zijn categorie staan." - visible_enabled: "Deze topic is nu zichtbaar en zal weer worden weergegeven in topiclijsten." - visible_disabled: "Deze topic is nu onzichtbaar en zal niet worden weergegeven in topiclijsten, en kan alleen via een directe link worden bezocht." + pinned_enabled: "Dit topic is nu vastgemaakt. Het verschijnt bovenaan de categorie ervan totdat het door een staflid wordt losgemaakt voor iedereen, of door individuele gebruikers voor henzelf." + pinned_disabled: "Dit topic is nu losgemaakt. Het verschijnt niet meer bovenaan de categorie ervan." + pinned_globally_enabled: "Dit topic is nu globaal vastgemaakt. Het verschijnt bovenaan de categorie ervan en alle topiclijsten totdat het door een staflid wordt losgemaakt voor iedereen, of door individuele gebruikers voor henzelf." + pinned_globally_disabled: "Dit topic is nu losgemaakt. Het verschijnt niet meer bovenaan de categorie ervan." + visible_enabled: "Dit topic is nu zichtbaar. Het wordt in topiclijsten weergegeven." + visible_disabled: "Dit topic is nu onzichtbaar. Het wordt niet meer in topiclijsten weergegeven. De enige manier om dit topic te zien, is via een directe koppeling." login: - not_approved: "Je account is nog niet goedgekeurd. Je zal via de mail een bericht van ons ontvangen wanneer je in kan loggen." + not_approved: "Uw account is nog niet goedgekeurd. U ontvangt een melding via e-mail zodra u zich kunt aanmelden." incorrect_username_email_or_password: "Incorrecte gebruikersnaam, e-mailadres of wachtwoord" wait_approval: "Bedankt voor je inschrijving. We zullen je laten weten als je account is goedgekeurd." active: "Je account is actief en klaar voor gebruik!" - activate_email: "

    Je bent er bijna! We hebben een activatiemail verstuurd naar %{email}. Volg de instructies in de mail om je account te activeren.

    Als de mail niet aankomt, check je spam folder, of probeer opnieuw aan te melden om een nieuwe activatiemail te versturen.

    " not_activated: "U kunt zich nog niet aanmelden. We hebben u een activeringsmail gestuurd. Volg de instructies in het e-mailbericht om uw account te activeren." not_allowed_from_ip_address: "U kunt zich niet als %{username} aanmelden vanaf dat IP-adres." admin_not_allowed_from_ip_address: "U kunt zich niet als beheerder aanmelden vanaf dat IP-adres." @@ -1230,7 +1315,7 @@ nl: password_too_long: "Wachtwoorden mogen maximaal 200 tekens lang zijn." email_too_long: "De e-mail die je hebt opgegeven is te lang. Mailbox namen mogen niet langer zijn dan 254 tekens, en domeinnamen niet langer dan 253 tekens." reserved_username: "Die gebruikersnaam is niet toegestaan." - missing_user_field: "Je hebt niet alle velden ingevuld." + missing_user_field: "U hebt niet alle gebruikersvelden ingevuld" close_window: "Authenticatie is voltooid. Sluit dit venster om verder te gaan." user: no_accounts_associated: "Geen accounts gekoppeld" @@ -1260,13 +1345,11 @@ nl: subject_template: "%{invitee_name} heeft je uitgenodigd voor %{site_domain_name}" invite_password_instructions: subject_template: "Geeft wachtwoord voor je %{site_name} account" + queued_posts_reminder: text_body_template: | - Bedankt voor het accepteren van je uitnodiging bij %{site_name} -- welkom! + Hallo, - Klik deze link om nu een wachtwoord te kiezen: - %{base_url}/users/password-reset/%{email_token} - - (Als bovenstaande link is verlopen, kies dan "Ik ben mijn wachtwoord vergeten" tijdens het inloggen met je e-mail adres.) + Er zijn berichten van nieuwe gebruikers voor moderatie achtergehouden die momenteel op goedkeuring wachten. [Keur ze goed of weiger ze hier](%{base_url}/queued-posts). flag_reasons: off_topic: "Je bericht is gemarkeerd als **off-topic**: de gemeenschap meent het niet goed past bij het onderwerp, zoals momenteel bepaald door de titel en de eerste post." inappropriate: "Je bericht is gemarkeerd als **ongepast**: de gemeenschap vindt het bericht beledigend, grof, of een schending van [onze gemeenschapsrichtlijnen](/guidelines)." @@ -1278,7 +1361,6 @@ nl: disagreed: "Bedankt dat je het ons hebt laten weten. We gaan er naar kijken." deferred: "Bedankt dat je het ons hebt laten weten. We gaan er naar kijken." deferred_and_deleted: "Bedankt dat je het ons hebt laten weten. We hebben het bericht verwijderd." - temporarily_closed_due_to_flags: "Dit topic is tijdelijk gesloten vanwege een groot aantal vlaggen vanuit de gebruikersgroep" system_messages: welcome_user: subject_template: "Welkom bij %{site_name}!" @@ -1340,11 +1422,11 @@ nl: We konden geen accounts vinden die overeenkomen met je e-mailadres. Probeer de e-mail van een ander adres te sturen, of neem contact op met de staf. email_reject_empty: text_body_template: |+ - Het spijt ons, maar het plaatsen van je e-mail op %{destination} (met onderwerp %{former_title}) is niet gelukt. + Het spijt ons, maar het plaatsen van uw e-mailbericht naar %{destination} (getiteld %{former_title}) is niet gelukt. - We konden geen bruikbare tekst in je e-mail vinden. + We konden geen antwoordtekst in uw e-mail vinden. - Als je deze melding krijgt en _wel_ een reactie had gestuurd, kan je het opnieuw proberen met een eenvoudigere opmaak. + Als u deze melding ziet en _wel_ een antwoord had bijgevoegd, kunt u het opnieuw proberen met een eenvoudigere opmaak. email_reject_parsing: @@ -1354,18 +1436,18 @@ nl: We konden je reactie niet in je e-mail vinden. **Zorg ervoor dat je reactie bovenaan de e-mail staat**, we kunnen ingebedde reacties niet verwerken. email_reject_invalid_post: text_body_template: | - Het spijt ons, maar het plaatsen van je e-mail op %{destination} (met onderwerp %{former_title}) is niet gelukt. + Het spijt ons, maar het plaatsen van uw e-mailbericht naar %{destination} (getiteld %{former_title}) is niet gelukt. - Dit kan bijvoorbeeld komen door complexe opmaak of door een te groot of te klein bericht. Probeer het opnieuw of plaats je bericht via de website als het nog steeds niet lukt. + Enkele mogelijke oorzaken zijn: complexe opmaak, bericht te groot, bericht te klein. Probeer het opnieuw, of plaats uw bericht via de website als dit aanhoudt. email_reject_invalid_post_specified: text_body_template: | - Het spijt ons, maar het plaatsen van je e-mail op %{destination} (met onderwerp %{former_title}) is niet gelukt. + Het spijt ons, maar het plaatsen van uw e-mailbericht naar %{destination} (getiteld %{former_title}) is niet gelukt. Reden: %{post_error} - Probeer het opnieuw als je het probleem kan oplossen. + Als u het probleem kunt oplossen, probeer het dan opnieuw. email_reject_invalid_post_action: text_body_template: | Het spijt ons, maar het plaatsen van je e-mail op %{destination} (met onderwerp %{former_title}) is niet gelukt. @@ -1395,9 +1477,9 @@ nl: text_body_template: | Hallo, - Dit is een automatisch bericht van %{site_name} om je te informeren dat de staff naar je account hebben gekeken en je account is nu niet langer geblokkeerd. + Dit is een automatisch bericht van %{site_name} om u te laten weten dat uw account na beoordeling door een staflid niet meer is geblokkeerd. - Je kan weer nieuwe topics beginnen en reageren op bestaande topics. Bedankt voor je geduld. + U kunt nu weer nieuwe antwoorden en topics aanmaken. Bedankt voor uw geduld. pending_users_reminder: subject_template: one: "1 gebruiker wacht op goedkeuring" @@ -1421,128 +1503,102 @@ nl: posted_by: "Geplaatst door %{username} op %{post_date}" digest: why: "Een korte samenvatting van %{site_link} sinds we je voor het laatst zagen op %{last_seen_at}." + since_last_visit: "Sinds uw laatste bezoek" + new_topics: "Nieuwe topics" + unread_messages: "Ongelezen berichten" + unread_notifications: "Ongelezen meldingen" liked_received: "Ontvangen likes" new_posts: "Nieuwe berichten" new_users: "Nieuwe gebruikers" popular_topics: "Populaire topics" follow_topic: "Dit topic volgen" + join_the_discussion: "Meer lezen" popular_posts: "Populaire berichten" + more_new: "Nieuw voor u" click_here: "klik hier" - forgot_password: - text_body_template: | - Iemand heeft aangegeven je wachtwoord te willen herstellen op [%{site_name}](%{base_url}). - - Ben jij dit niet, dan kan je zonder problemen deze e-mail negeren. - - Klik op de volgende link om een nieuw wachtwoord te kiezen: - %{base_url}/users/password-reset/%{email_token} + preheader: "Een korte samenvatting sinds uw laatste bezoek op %{last_seen_at}" + mailing_list: + why: "Alle activiteit op %{site_link} voor %{date}" + new_topics: "Nieuwe topics" + topic_updates: "Topicupdates" + view_this_topic: "Dit topic bekijken" + back_to_top: "Terug naar boven" set_password: - text_body_template: | - Iemand heeft verzocht een wachtwoord aan je account toe te voegen op [%{site_name}](%{base_url}). Je kan ook nog - indien geactiveerd - inloggen met een online service (Google, Facebook, etc) waar je met hetzelfde mailadres geregistreerd staat. - - Als je dit verzoek niet van jou kwam, kun je deze mail negeren. - - Klik op de volgende link om een wachtwoord in te stellen: - %{base_url}/users/password-reset/%{email_token} + title: "Wachtwoord instellen" admin_login: - text_body_template: | - Iemand heeft geprobeerd om met je account op [%{site_name}](%{base_url}) in te loggen. - - Als jij het niet was, kan je deze e-mail gewoon negeren. - - Klik op de volgende link om in te loggen: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Er is een nieuw account voor je aangemaakt op %{site_name} - - Klik op deze link om een wachtwoord in te stellen voor je nieuwe account: - %{base_url}/users/password-reset/%{email_token} + title: "Beheerdersaanmelding" signup_after_approval: subject_template: "Je inschrijving op %{site_name} is geaccepteerd!" - text_body_template: | - Welkom op %{site_name}! - - Een staflid heeft je account op %{site_name} goedgekeurd. - - Klik op de volgende link om je nieuwe account te bevestigen en activeren: - %{base_url}/users/activate-account/%{email_token} - - Als de bovenstaande link niet is aan te klikken, probeer deze dan te kopiëren en te plakken in de adresregel van je webbrowser. - - %{new_user_tips} - - Wij geloven in de volgende richtlijnen: [civilized community behavior](%{base_url}/guidelines) - - Veel plezier! - - (Als je contact wilt opnemen met [staff members](%{base_url}/about) als nieuwe gebruiker, stuur dan een reply op dit bericht.) - signup: - text_body_template: | - Welkom op %{site_name}! - - Klik op de volgende link om je nieuwe account te bevestigen en te activeren: - %{base_url}/users/activate-account/%{email_token} - - Mocht je de bovenstaande link niet kunnen aanklikken, probeer deze dan te kopiëren en te plakken in de adresbalk van je web browser. page_not_found: title: "Oeps! Die pagina bestaat niet of is privé." popular_topics: "Populair" recent_topics: "Recent" see_more: "Meer" - search_title: "Zoek op deze site" + search_title: "Zoeken op deze website" search_google: "Google" login_required: welcome_message: | #[Welkom op %{title}](#welcome) Voor dit forum is een account nodig. Maak een account aan of log in om verder te gaan. terms_of_service: - title: "Algemene Voorwaarden" - signup_form_message: 'Ik heb de Algemene Voorwaarden gelezen en ga hiermee akkoord.' + title: "Servicevoorwaarden" + signup_form_message: 'Ik heb de Servicevoorwaarden gelezen en ga hiermee akkoord.' deleted: 'verwijderd' upload: - edit_reason: "download lokale kopieën van afbeeldingen" - unauthorized: "Sorry, je mag dat bestand niet uploaden (toegestane extensies: %{authorized_extensions})." + edit_reason: "lokale kopieën van afbeeldingen gedownload" + unauthorized: "Sorry, het bestand dat u probeert te uploaden is niet toegestaan (toegestane extensies: %{authorized_extensions})." pasted_image_filename: "Geplakte afbeelding" store_failure: "Het opslaan van upload #%{upload_id} voor gebruiker #%{user_id} is mislukt." - file_missing: "Sorry, maar je moet een bestand selecteren om te uploaden." + file_missing: "Sorry, u moet een bestand opgeven om te uploaden." attachments: too_large: "Sorry, het bestand dat u probeert te uploaden is te groot (maximumgrootte is %{max_size_kb}%KB)." images: too_large: "Sorry, de afbeelding die u probeert te uploaden is te groot (maximumgrootte is %{max_size_kb}%KB). Verklein de afbeelding en probeer het opnieuw." size_not_found: "Het is niet gelukt de afmetingen van de afbeelding te bepalen. Misschien is het bestand corrupt?" + avatar: + missing: "Sorry, we kunnen geen avatar vinden die met dat e-mailadres is gekoppeld. Kunt u proberen deze opnieuw te uploaden?" + flag_reason: + sockpuppet: "Een nieuwe gebruiker heeft een topic aangemaakt, en een andere nieuwe gebruiker op hetzelfde IP-adres (%{ip_address}) heeft geantwoord. Bekijk de website-instelling 'flag_sockpuppets'." + spam_hosts: "Deze nieuwe gebruiker heeft geprobeerd meerdere berichten met koppelingen naar hetzelfde domein (%{domain}) aan te maken. Bekijk de website-instelling newuser_spam_host_threshold." email_log: + post_user_deleted: "Gebruiker van het bericht is verwijderd." no_user: "Kan geen gebruiker met id %{user_id} vinden" anonymous_user: "Gebruiker is anoniem" suspended_not_pm: "Gebruiker is geschorst, geen bericht" - seen_recently: "Gebruiker was recentelijk nog online" + seen_recently: "Gebruiker is onlangs nog gezien" post_not_found: "Kan geen bericht met id %{post_id} vinden" - notification_already_read: "De notificatie waar deze mail voor is is al gezien" + notification_already_read: "De melding waar deze e-mail over gaat is al gelezen" topic_nil: "bericht.topic is leeg" - post_deleted: "bericht was verwijderd door de gebruiker" + post_deleted: "bericht is verwijderd door de schrijver" user_suspended: "gebruiker was geschorst" already_read: "gebruiker heeft dit bericht al gelezen" + exceeded_emails_limit: "max_emails_per_day_per_user overschreden" + exceeded_bounces_limit: "bounce_score_threshold overschreden" message_blank: "bericht is leeg" - message_to_blank: "bericht.aan is leeg" + message_to_blank: "message.to is leeg" text_part_body_blank: "text_part.body is leeg" body_blank: "geen tekst" + no_echo_mailing_list_mode: "Mailinglijstmeldingen voor eigen berichten van gebruiker uitgeschakeld" color_schemes: base_theme_name: "Basis" about: "Over" - guidelines: "Regels" + guidelines: "Richtlijnen" privacy: "Privacy" - edit_this_page: "Wijzig deze pagina" + edit_this_page: "Deze pagina bewerken" csv_export: boolean_yes: "Ja" boolean_no: "Nee" static_topic_first_reply: | - Verander het eerste bericht in dit Topic om de inhoud van de %{page_name} pagina te wijzigen. + Bewerk het eerste bericht in dit topic om de inhoud van de pagina %{page_name} te wijzigen. guidelines_topic: - title: "FAQ/Regels" + title: "FAQ/Richtlijnen" tos_topic: title: "Algemene Voorwaarden" privacy_topic: title: "Privacy Voorwaarden" + badges: + read_guidelines: + name: Richtlijnen gelezen admin_login: success: "E-mail verstuurd" error: "Fout!" @@ -1551,12 +1607,11 @@ nl: discourse_hub: access_token_problem: "Licht een admin in: Gelieve de site instellingen bijwerken met de juiste discourse_org_access_key." performance_report: - initial_post_raw: Deze topic bevat dagelijkse performancerapporten van je site + initial_post_raw: Dit topic bevat dagelijkse prestatierapporten voor uw website. initial_topic_title: Performancerapportages van de website finish_installation: congratulations: "Gefeliciteerd, u hebt Discourse geïnstalleerd!" safe_mode: - no_customizations: "Alle website-aanpassingen uitschakelen" only_official: "Niet-officiële plug-ins uitschakelen" no_plugins: "Alle plug-ins uitschakelen" wizard: @@ -1593,7 +1648,7 @@ nl: title: "Emoji" invites: title: "Staf uitnodigen" - description: "Je bent bijna klaar! Nodig wat stafleden uit om te helpen met het starten van discussies met interessante topics en berichten om je gemeenschap op te zetten." + description: "U bent bijna klaar! Nodig wat stafleden uit om te helpen met het starten van discussies met interessante topics en berichten om uw gemeenschap op te zetten." finished: title: "Uw Discourse is gereed!" description: | diff --git a/config/locales/server.pl_PL.yml b/config/locales/server.pl_PL.yml index 24bc5fd490..856eca8074 100644 --- a/config/locales/server.pl_PL.yml +++ b/config/locales/server.pl_PL.yml @@ -27,7 +27,6 @@ pl_PL: topics: "Tematy" posts: "posty" loading: "Ładowanie" - powered_by_html: 'Zasilane przez Discourse, najlepiej oglądać z włączonym JavaScriptem' log_in: "Logowanie" purge_reason: "Automatycznie usunięte jako porzucone, dezaktywowane konto." disable_remote_images_download_reason: "Pobieranie zewnętrznych grafik zostało wyłączone z uwagi na niską ilość wolnego miejsca na dysku." @@ -290,14 +289,6 @@ pl_PL: Ten temat jest dla Ciebie istotny – napisałeś tu ponad %{percent}% wszystkich odpowiedzi. Czy na pewno dajesz innym wystarczająco czasu na przedstawienie swoich punktów widzenia? - get_a_room: | - ### Rozważ odpowiadanie większej liczbie osób - - Dotychczas odpowiedziałeś %{count} razy @%{reply_username} w tym temacie. - - Czy rozważałeś odpowiadanie *innym* ludziom biorącym udział w dyskusji? Na dobrą dyskusję składa się wiele głosów i perspektyw. - - Jeśli chciałbyś kontynuować dłuższą rozmowę z tym użytkownikiem, [wyślij im prywatną wiadomość](/users/%{reply_username}). too_many_replies: | ### Osiągnąłeś limit odpowiedzi w tym temacie @@ -1310,7 +1301,6 @@ pl_PL: incorrect_username_email_or_password: "Niepoprawna nazwa użytkownika, email lub hasło" wait_approval: "Dziękujemy za rejestrację. Powiadomimy Cię gdy Twoje konto zostanie aktywowane." active: "Twoje konto zostało aktywowane i jest gotowe do użycia." - activate_email: "

    Prawie gotowe! Wysłaliśmy email aktywacyjny na adres %{email}. Postępuj zgodnie z instrukcją z tego emaila, żeby dokończyć ten proces.

    Jeśli email nie dotrze do Ciebie, sprawdź folder ze spamem, lub zaloguj sie jeszcze raz, żeby wysłać go ponownie.

    " not_activated: "Nie możesz się jeszcze zalogować. Wysłaliśmy do Ciebie email aktywacyjny. Wykonaj instrukcje w nim zawarte by aktywować Twoje konto." not_allowed_from_ip_address: "Nie możesz się zalogować jako %{username} z tego adresu IP." admin_not_allowed_from_ip_address: "Nie możesz zalogować się jako administrator z tego adresu IP." @@ -1424,19 +1414,6 @@ pl_PL: %{invite_link} invite_password_instructions: subject_template: "Ustaw hasło dla swojego konta na %{site_name}" - text_body_template: |+ - Dziękujemy za zaakceptowanie zaproszenia do %{site_name} -- witamy! - - - - Kliknij ten link, by wybrać hasło: - - %{base_url}/users/password-reset/%{email_token} - - - - (Jeśli powyższy link wygasł, wybierz "Zapomniałem hasła", logując się za pomocą adresu email.) - test_mailer: text_body_template: | To testowy mail z serwisu: @@ -1523,7 +1500,6 @@ pl_PL: disagreed: "Dziękujemy za zwrócenie uwagi. Zajmiemy się tym wkrótce." deferred: "Dziękujemy za zwrócenie uwagi. Zajmiemy się tym wkrótce." deferred_and_deleted: "Dziękujemy za zwrócenie uwagi. Wpis został usunięty." - temporarily_closed_due_to_flags: "Ten temat został tymczasowo zamknięty z uwagi na wysoką liczbę oflagowań wykonanych przez społeczność." system_messages: post_hidden: title: "Ukryty post" @@ -1831,6 +1807,7 @@ pl_PL: join_the_discussion: "Czytaj więcej" popular_posts: "Popularne wpisy" more_new: "Nowe dla ciebie" + subject_template: "[%{email_prefix}] Podsumowanie" click_here: "kliknij tutaj" from: "%{site_name} podsumowanie" preheader: "Podsumowanie aktywności od Twojej ostatniej wizyty w dniu %{last_seen_at}" @@ -1860,33 +1837,8 @@ pl_PL: signup_after_approval: title: "Zarejestruj się po zatwierdzeniu" subject_template: "Zostałeś zaakceptowany na forum %{site_name}!" - text_body_template: |+ - Witamy na %{site_name}! - - Nasz personel zatwierdził twoje konto na %{site_name}. - - Kliknij na podany link, aby potwierdzić i aktywować swoje nowe konto: - %{base_url}/users/activate-account/%{email_token} - - Jeśli podany link nie działa, spróbuj skopiować go i przykleić w pasku adresów swojej wyszukiwarki. - - %{new_user_tips} - - Wierzymy, że [cywilizowana dyskusja](%{base_url}/guidelines) jest zawsze możliwa. - - Baw się dobrze! - - (Jeśli potrzebujesz skontaktować się z [członkiem zespołu](%{base_url}/about) jako nowy użytkownik, odpowiedz na tę wiadomość.) - signup: title: "Podpis" - text_body_template: | - Witaj na %{site_name}! - - Kliknij na poniższy link by potwierdzić i aktywować Twoje nowe konto: - %{base_url}/users/activate-account/%{email_token} - - Jeśli powyższy link nie jest klikalny, spróbuj skopiować i wkleić go do pasku adresu Twojej przeglądarki. page_not_found: title: "Ups! Ta strona nie istnieje lub jest prywatna." popular_topics: "Popularne" @@ -2125,7 +2077,6 @@ pl_PL: message: "

    Powtórnie przesłaliśmy maila aktywacyjnego do %{email}" safe_mode: title: "Wejście w tryb bezpieczny" - no_customizations: "Wyłącz własną personalizację strony" only_official: "Wyłącz nieoficjalne wtyczki" no_plugins: "Wyłącz wszystkie wtyczki" enter: "Włącz Tryb Bezpieczny" diff --git a/config/locales/server.pt.yml b/config/locales/server.pt.yml index 3bb384932b..3e589073ca 100644 --- a/config/locales/server.pt.yml +++ b/config/locales/server.pt.yml @@ -27,7 +27,6 @@ pt: topics: "Tópicos" posts: "mensagens" loading: "A carregar" - powered_by_html: 'Desenvolvido por Discourse, e melhor visualizado com o JavaScript ativo' log_in: "Iniciar Sessão" purge_reason: "Conta removida automaticamente como conta abandonada, desactivada." disable_remote_images_download_reason: "O download remoto de imagens foi desativado por não haver espaço disponível no disco." @@ -268,16 +267,12 @@ pt: attributes: hex: invalid: "não é uma cor válida" - post_reply: - base: - different_topic: "Publicação e resposta devem pertencer ao mesmo tópico." web_hook: attributes: payload_url: invalid: "URL inválido. O URL deve incluir http:// ou https://. E espaços não são permitidos." <<: *errors user_profile: - no_info_me: "

    o campo Sobre Mim do seu perfil está em branco, quer preenchê-lo?
    " no_info_other: "
    %{name} ainda não colocou nada no campo Sobre Mim
    " vip_category_name: "Salão" vip_category_description: "Uma categoria exclusiva para membros de Nível de Confiança 3 ou superior." @@ -1301,7 +1296,6 @@ pt: incorrect_username_email_or_password: "Utilizador, email ou palavra-passe incorretos" wait_approval: "Obrigado por se inscrever. Será notificado por email quando a sua conta for aprovada." active: "A sua conta está ativa e pronta para ser utilizada." - activate_email: "

    Está quase no final! Enviámos um email de ativação para %{email}. Por favor siga as instruções no email para ativar a sua conta.

    Se não receber o email, verifique a sua pasta de spam, ou tente autenticar-se novamente para enviar outro email de ativação.

    " not_activated: "Ainda não pode iniciar sessão. Enviámos um email de ativação para si. Por favor siga as instruções no email para ativar a sua conta." not_allowed_from_ip_address: "Não pode iniciar sessão como %{username} a partir desse endereço IP." admin_not_allowed_from_ip_address: "Não pode iniciar sessão como administrador a partir desse endereço IP." @@ -1359,13 +1353,6 @@ pt: subject_template: "%{invitee_name} convidou-o para %{site_domain_name}" invite_password_instructions: subject_template: "Configurar a palavra-passe para a sua conta %{site_name}" - text_body_template: | - Obrigado por aceitar o seu convite para %{site_name} -- bem-vindo! - - Clique nesta hiperligação para escolher uma nova palavra-passe: - %{base_url}/users/password-reset/%{email_token} - - (Se a hiperligação acima tiver expirado, escolha "Esqueci a minha palavra-passe" ao iniciar sessão com o seu endereço de email.) test_mailer: text_body_template: | Este é um email de teste de @@ -1452,7 +1439,6 @@ pt: disagreed: "Obrigado por nos informar. Estamos a analisá-lo." deferred: "Obrigado por nos informar. Estamos a analisá-lo." deferred_and_deleted: "Obrigado por nos informar. Removemos a mensagem." - temporarily_closed_due_to_flags: "Este tópico está fechado temporariamente devido a um grande número de bandeiras da comunidade. " system_messages: post_hidden: subject_template: "Publicação oculta devido a denúncias da comunidade" @@ -1910,51 +1896,6 @@ pt: topic_updates: "Atualizações de tópico" view_this_topic: "Ver este tópico" back_to_top: "Voltar ao topo" - forgot_password: - text_body_template: | - Alguém pediu para redefinir a sua palavra-passe em [%{site_name}](%{base_url}). - - Se este pedido não veio da sua parte, por favor ignore este email. - - Para escolher a sua nova palavra-passe clique na seguinte hiperligação: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Alguém pediu para adicionar uma palavra-passe à sua conta em [%{site_name}](%{base_url}). Como alternativa, pode entrar usando qualquer serviço online suportado (Google, Facebook, etc) que estiver associado a este endereço de email validado. - - Se não fez este pedido, pode ignorar este email. - - Clique na hiperligação seguinte para escolher uma palavra-passe. - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - Alguém pediu para iniciar sessão na sua conta em [%{site_name}](%{base_url}). - - Se não fez este pedido, pode ignorar este email com segurança. - - Clique na seguinte hiperligação para iniciar sessão: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Uma nova conta foi criada para si em %{site_name} - - Clique na hiperligação seguinte para escolher uma palavra-passe para a sua nova conta. - %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - text_body_template: | - Confirme o seu novo endereço de email para %{site_name} ao clicar na seguinte ligação: - - %{base_url}/users/authorize-email/%{email_token} - confirm_old_email: - text_body_template: | - Antes de podermos alterar o seu endereço de email, precisámos que confirme que - controla - a conta de email atual. Após completar este passo, terá de confirmar - o seu novo endereço de email. - - Confirme o seu endereço de email para o %{site_name} ao clicar na seguinte ligação: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: text_body_template: | Isto é uma mensagem automática para o informar que o seu endereço de email para @@ -1966,31 +1907,6 @@ pt: %{new_email} signup_after_approval: subject_template: "Foi aprovado no %{site_name}!" - text_body_template: | - Bem-vindo a %{site_name}! - - Um membro do pessoal aprovou a sua conta em %{site_name}. - - Clique na seguinte ligação para confirmar e ativar a sua nova conta: - %{base_url}/users/activate-account/%{email_token} - - Se a ligação acima não for clicável, tente copiá-la e colá-la na barra de endereço do seu navegador de internet. - - %{new_user_tips} - - Nós acreditamos em [comportamento civilizado da comunidade](%{base_url}/guidelines) em todas as alturas. - - Desfrute da sua estadia! - - (Se precisar de comunicar com [membros da equipa de apoio](%{base_url}/about) como um novo utilizador, apenas responda a esta mensagem.) - signup: - text_body_template: | - BeM-vindo ao %{site_name}! - - Clique na seguinte hiperligação para confirmar e ativar a sua nova conta: - %{base_url}/users/activate-account/%{email_token} - - Se a hiperligação não for clicável, tente copiá-la e colá-la na barra de endereço do seu navegador de internet. page_not_found: title: "Oops! Essa página não existe or é privada." popular_topics: "Popular" @@ -2478,7 +2394,6 @@ pt: Este distintivo é concedido quando pela primeira vez adiciona um Emoji à sua publicação :thumbsup:. Emoji deixam-no exprimir emoção nas suas publicações, de felicidade :smiley: a tristeza :anguished: a raiva :angry: e tudo entre estes :sunglasses: . Escreva simplesmente : (dois pontos) ou prima o botão de Emoji na barra de ferramentas no editor para escolher um de entre centenas de escolhas :ok_hand: first_mention: name: Primeira Menção - description: Mencionou um utilizador numa Publicação long_description: Este distintivo é concedido pela primeira vez que menciona o @nome-de-utilizador de alguém numa publicação sua. Cada menção gera uma notificação para o mencionado, de maneira a que saibam da sua publicação. Simplesmente comece por escrever @ (símbolo arroba) para mencionar qualquer utilizador, ou, se permitido, um grupo – é uma maneira conveniente de chamar a atenção de alguém. first_onebox: name: Primeira Pré-visualização @@ -2486,7 +2401,6 @@ pt: long_description: Este distintivo é concedido pela primeira vez que publica uma ligação numa linha por si próprio, que foi então automaticamente expandido numa pré-visualização com um breve sumário da ligação, um titulo, e (quando disponível) uma imagem. first_reply_by_email: name: Primeira Resposta por Email - description: Respondeu a uma Publicação por email long_description: | Este distintivo é concedido pela primeira vez que responde a uma publicação por email :e-mail:. admin_login: @@ -2520,7 +2434,6 @@ pt: safe_mode: title: "Entrar em modo de segurança" description: "O modo de segurança permite-lhe testar o seu sítio sem carregar plugins ou alterações." - no_customizations: "Desativar todas as alterações do sítio" only_official: "Desativar plugins não-oficiais" no_plugins: "Desativar todos os plugins" enter: "Entrar em Modo de Segurança" diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index 85c50f33f4..283d4883a1 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -27,7 +27,6 @@ pt_BR: topics: "Tópicos" posts: "mensagens" loading: "Carregando" - powered_by_html: 'Movido a Discourse, melhor visualizado com JavaScript ativado' log_in: "Entrar" purge_reason: "Automaticamente eliminada por ser uma conta abandonada, não ativada" disable_remote_images_download_reason: "Download de imagens remotas foi desativado porque não havia espaço suficiente em disco disponível." @@ -262,14 +261,6 @@ pt_BR: Este tópico claramente é importante para você – você postou mais de %{percent}% das respostas aqui. Tem certeza que você está dando tempo para que outras pessoas também compartilhem seus pontos de vista? - get_a_room: | - ### Considere responder para mais pessoas - - Você já respondeu %{count} vezes o usuário @%{reply_username} neste tópico em particular. - - Já pensou em responder também *outras* pessoas nesta discussão? Uma boa discussão envolve várias vozes e perspectivas. - - Se quiser continuar estendendo sua conversa com este usuário, [envie uma mensagem pessoal para ele](/users/%{reply_username}). too_many_replies: | ### Você atingiu o limite de respostas para este tópico @@ -318,16 +309,12 @@ pt_BR: attributes: hex: invalid: "Não é uma cor válida" - post_reply: - base: - different_topic: "Postagem e réplica precisam pertencer ao mesmo tópico." web_hook: attributes: payload_url: invalid: "URL inválida. A URL deve conter http:// ou https://. Espaço em branco não é permitido." <<: *errors user_profile: - no_info_me: "
    o campo Sobre mim do seu perfil está em branco, quer completar?
    " no_info_other: "
    %{name} ainda não colocou nada no campo Sobre Mim
    " vip_category_name: "Salão" vip_category_description: "Uma categoria exclusiva para membros com nível de confiança 3 e superior." @@ -1236,7 +1223,6 @@ pt_BR: incorrect_username_email_or_password: "Usuário, email ou senha incorretos" wait_approval: "Obrigado por se registar. Você será notificado por email quando a sua conta for aprovada." active: "A sua conta está ativa e pronta." - activate_email: "

    Você está quase lá! Enviamos um email de ativação para %{email}. Por favor, siga as instruções contidas nele para ativar sua conta.

    Se ele não chegar, verifique sua pasta de spam, ou tente entrar novamente para enviar outro email de ativação.

    " not_activated: "Ainda não podes fazer log in. Enviamos um email de ativação para você. Por favor siga as instruções no email para ativar a sua conta." not_allowed_from_ip_address: "Você não pode entrar como %{username} a partir deste endereço IP." admin_not_allowed_from_ip_address: "Você não pode entrar como administrador a partir deste endereço IP." @@ -1281,13 +1267,6 @@ pt_BR: subject_template: "%{invitee_name} convidou você para participar de %{site_domain_name}" invite_password_instructions: subject_template: "Defina uma senha para a sua %{site_name} conta" - text_body_template: | - Obrigado por aceitar o seu convite para %{site_name} -- Bem-Vindo! - - Clique neste link para escolher uma senha agora: - %{base_url}/users/password-reset/%{email_token} - - (Se o link acima expirou, escolha a opção "Esqueci minha senha" quando efetuar o login com o seu endereço de e-mail.) flag_reasons: off_topic: "Sua postagem foi sinalizada como **off-topic**: a comunidade sente que não é uma boa adequação ao tópico, como atualmante definido de acordo com o título e o primeiro post." inappropriate: "Sua postagem foi sinalizada como **não apropriada**: a comunidade sente que ela é ofensiva, abusiva, ou uma violação das [nossas orientações da comunidade](/guidelines)." @@ -1299,7 +1278,6 @@ pt_BR: disagreed: "Obrigado por nos avisar. Nós estamos olhando nele." deferred: "Obrigado por nos avisar. Nós estamos olhando nele." deferred_and_deleted: "Obrigado por nos avisar. Nós removemos o post." - temporarily_closed_due_to_flags: "Este tópico foi temporariamente fechado devido a um grande número de sinalizações da comunidade." system_messages: welcome_user: subject_template: "Bem-vindo ao %{site_name}!" @@ -1527,51 +1505,8 @@ pt_BR: topic_updates: "Atualizações do tópico" view_this_topic: "Ver esse tópico" back_to_top: "Voltar ao topo" - forgot_password: - text_body_template: | - Alguém pediu para redefinir a sua senha em [%{site_name}](%{base_url}). - - Se não foi você, por favor ignore este email. - - Para escolher a sua nova senha clique no link a seguir: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Alguém solicitou para adicionar uma senha para a sua conta em [%{site_name}] (% {base_url}). Alternativamente, você pode fazer login usando qualquer serviço online suportado (Google, Facebook, etc) que está associado a este endereço de e-mail validado. - - Se você não fez esse pedido, você pode ignorar este e-mail. - - Clique no link abaixo para escolher uma senha: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - Alguém pediu para logar na sua conta no [%{site_name}](%{base_url}). - - Se você não realizou esse pedido, você pode ignorar esse email com segurança. - - Clique no link a seguir para logar: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Uma nova conta foi criada para você em %{site_name} - - Clique no link a seguir para escolher uma senha para a sua nova conta: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "Confirmar novo e-mail" - text_body_template: | - Confirme seu novo endereço de email para %{site_name} clicando no link a seguir: - - %{base_url}/users/authorize-email/%{email_token} - confirm_old_email: - text_body_template: | - Antes que possamos mudar seu endereço de email, precisamos confirmar que você controla - a conta de email atual. Após completar esse passo, pediremos que você confirme - o novo endereço de email. - - Confirme seu endereço de email atual para %{site_name} clicando no link a seguir: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: text_body_template: | Esta é uma mensagem automática para lhe avisar que seu endereço de email para @@ -1583,31 +1518,6 @@ pt_BR: %{new_email} signup_after_approval: subject_template: "Você foi aprovado no %{site_name}!" - text_body_template: | - Bem vindo a %{site_name}! - - Um membro interno aprovou sua conta no %{site_name}. - - Selecione o endereço a seguir para confirmar e ativar sua nova conta: - %{base_url}/users/activate-account/%{email_token} - - Se preferir, tente copiar e colar o endereço acima no seu navegador. - - %{new_user_tips} - - Nós acreditamos sempre em [comportamento comunitario civilizado](%{base_url}/guidelines). - - Aproveite sua participação! - - (Se precisar se comunicar com um [membro interno](%{base_url}/about) como um novo usuário, só responder a esta mensagem.) - signup: - text_body_template: | - Ben-vindo ao %{site_name}! - - Clique no seguinte link para confirmar e aticar a sua nova conta: - %{base_url}/users/activate-account/%{email_token} - - Se não conseguires clicar no link em cima, tenta copia-lo e cola-lo na barra de endereções do seu browser de internet. page_not_found: title: "Oops! Essa página não existe ou é privada." popular_topics: "Popular" @@ -1806,7 +1716,6 @@ pt_BR: description: Utilizou um emoji em uma publicação first_mention: name: Primeira menção - description: Mencionou um usuário em uma publicação long_description: Este emblema é concedido na primeira vez em que você menciona o @nomedeusuaio de alguém em uma publicação. Cada menção gera uma notificação para a pessoa de forma que ela saiba o que você publicou. Simplesmente comece a digitar "@" (arroba) para mencionar qualquer usuário ou, caso permitido, um grupo -- é uma maneira conveniente de chamar a atenção de alguém. first_onebox: name: Primeiro Onebox @@ -1814,7 +1723,6 @@ pt_BR: long_description: 'Esse emblema é concedido na primeira vez em que você posta um link que é convertido automaticamente em uma caixa com um título, sumário e uma imagem (se disponível) sumarizando o link. ' first_reply_by_email: name: Primeira Resposta por email - description: Respondeu uma mensagem por email admin_login: success: "Email Enviado" error: "Erro!" diff --git a/config/locales/server.ro.yml b/config/locales/server.ro.yml index 817f310743..96642b7b30 100644 --- a/config/locales/server.ro.yml +++ b/config/locales/server.ro.yml @@ -27,13 +27,14 @@ ro: topics: "Subiecte" posts: "Postări" loading: "Se încarcă" - powered_by_html: 'Propulsat de Discourse, Vizualizare optimă cu JavaScript activat' log_in: "Autentificare" purge_reason: "Cont șters automat, ca fiind abandonat, dezactivat." disable_remote_images_download_reason: "Descărcarea de imagini la distanță a fost dezactivată deoarece nu mai era spațiu suficient pe disc." anonymous: "Anonime" + remove_posts_deleted_by_author: "Șters de către autor" emails: incoming: + default_subject: "Acest subiect are nevoie de un titlu" show_trimmed_content: "Arată conținut restrâns." maximum_staged_user_per_email_reached: "Număr maxim de utilizatori în așteptare, pe email." errors: @@ -176,6 +177,7 @@ ro: spamming_host: "Nu poți adăuga un link pentru acest domeniu." user_is_suspended: "Utilizatorii suspendați nu au dreptul de a posta." topic_not_found: "Ceva n-a mers bine. Poate că acest subiect a fost închis sau șters în timp ce te uitai la el?" + not_accepting_pms: "%{username} nu acceptă mesaje în acest moment." just_posted_that: "este prea asemănător cu ceea ce ați postat recent" invalid_characters: "conține caractere invalide" is_invalid: "pare neclar, e completă propoziția?" @@ -250,6 +252,20 @@ ro: - Critica constructivă e binevenită, critică *ideea*, nu oamenii. Pentru detalii, [verifică regulile comune](/reguli). Acest panel va apărea doar pentru prima %{education_posts_text}. + avatar: | + ### Ce zici de o poză atașată contului tău? + + Ai adăugat câteva comentarii și subiecte, dar poza ta de profil nu este unică, cum ești tu - este doar o literă. + + Ai luat în considerare să **[îți actualizezi profilul](%{profile_path})** cu o imagine care te reprezintă? + + Este mai ușor să urmărești o discuție și să găsești oameni interesanți într-o conversație atunci când fiecare are o imagine unică! + reviving_old_topic: | + ### Reînvii această discuție? + + Ultimul răspuns în această discuție a fost în urmă cu **%{days}** zile. Răspunsul tău va pune discuția pe prima pagină și îi va notifica pe toți cei ce au fost implicați în discuție. + + Ești sigur că vrei să continui această discuție veche? activerecord: attributes: category: @@ -280,22 +296,27 @@ ro: same_as_username: "este identică cu nume utilizator. Te rugăm să folosești o parolă mai sigură." same_as_email: "este identică cu emailul. Te rugăm să folosești o parolă mai sigură." same_as_current: "este identică cu parola curentă." + unique_characters: "are prea multe caractere repetate. Te rugăm să folosești o parolă mai sigură." ip_address: signup_not_allowed: "Înscrierea de pe acest cont nu este permisă." color_scheme_color: attributes: hex: invalid: "nu este o culoare validă" - post_reply: - base: - different_topic: "Postarea și răspunsul trebuie să aparțină aceluiași subiect." web_hook: attributes: payload_url: invalid: "URL-ul este invalid. URL-ul trebuie să includă http:// sau https:// , iar caracterul spațiu nu este permis." + custom_emoji: + attributes: + name: + taken: este deja folosit de un alt emoji + topic_status_update: + attributes: + execute_at: + in_the_past: "trebuie să fie în viitor." <<: *errors user_profile: - no_info_me: "
    câmpul Despre mine din profilul tău este momentan gol. Dorești să-l completezi?
    " no_info_other: "
    %{name} încă nu a introdus nimic în câmpul Despre mine din profilul său
    " vip_category_name: "Sufragerie" vip_category_description: "O categorie dedicată exclusiv celor cu nivelul de încredere 3 sau mai mare" @@ -483,6 +504,8 @@ ro: other: "cu aproape %{count} de ani în urmă" password_reset: no_token: "Ne pare rău, dar link-ul de schimbare a parolei este prea vechi. Selectează butonul Înregistrare și folosește 'Mi-am uitat parola' pentru a obține un nou link." + choose_new: "Alege o parolă nouă" + choose: "Alege o parolă" update: 'Actualizează parola' save: 'Setează parola' title: 'Resetează parola' @@ -505,6 +528,13 @@ ro: welcome_to: "Bine ai venit pe %{site_name}!" approval_required: "Un moderator trebuie să aprobe manual contul înainte să poți accesa forumul. Vei primi un email imediat ce contul ți-a fost aprobat!" missing_session: "Nu putem detecta dacă contul tău a fost deja creat. Te rugăm să verifici că ai cookie-urile activate. " + activated: "Acest cont a fost deja activat." + admin_confirm: + title: "Confirmă contul de Administrator" + description: "Eșți sigur că vrei ca %{target_username} să fie administrator?" + grant: "Acordă titlu de admin" + complete: "%{target_username} este administrator." + back_to: "Întoarce-te la %{title}" post_action_types: off_topic: title: 'În afara subiectului' @@ -1361,7 +1391,6 @@ ro: incorrect_username_email_or_password: "Nume de utilizator, email sau parolă incorecte" wait_approval: "Îți mulțumim pentru autentificare. Te vom anunța când contul dumneavoastră a fost aprobat." active: "Contul tău a fost activat și e gata să fie folosit." - activate_email: "Ești aproape gata! Am trimis un email de activare către %{email}. Te rugăm să urmezi instrucțiunile din email pentru a-ți activa contul." not_activated: "Nu te poți încă autentifica. Ți-am trimis un email de autentificare. Te rugăm să urmezi instrucțiunile din email pentru a-ți activa contul." not_allowed_from_ip_address: "Nu te poți conecta ca %{username} de la această adresa de IP." admin_not_allowed_from_ip_address: "Nu te poți conecta ca administrator de la această adresa de IP." @@ -1421,13 +1450,6 @@ ro: subject_template: "%{invitee_name} te-a invitat să te alături pe %{site_domain_name}" invite_password_instructions: subject_template: "Setează parolă pentru contul tău %{site_name}" - text_body_template: | - Îți mulțumim că ai acceptat invitația pe %{site_name} -- bun venit! - - Acum dă click pe acest link pentru a-ți alege o parolă: - %{base_url}/users/password-reset/%{email_token} - - (Dacă linkul de mai sus a expirat, alege "Mi-am uitat parola" la autentificarea cu adresa ta de email.) test_mailer: text_body_template: | Acesta este un email de test de la @@ -1514,7 +1536,6 @@ ro: disagreed: "Îți mulțumim pentru avertizare. O să aruncăm o privire." deferred: "Îți mulțumim că ne-ai informat. O să aruncăm o privire." deferred_and_deleted: "Îți mulțumim că ne-ai informat. Am șters postarea." - temporarily_closed_due_to_flags: "Acest subiect este temporar închis din cauza numărului mare de marcaje de avertizare primite din partea comunității." system_messages: post_hidden: subject_template: "Postare ascunsă din cauza marcajelor de avertizare ale comunității" @@ -1529,14 +1550,9 @@ ro: Mai mulți membri ai comunității au marcat acestă postare cu marcaje de avertizare înainte ca ea să fie ascunsă, așa că te rugăm să îți revizuiești postarea pentru a ține cont de feedback-ul primit. **Îți poți edita postarea după %{edit_delay} minute, și ea va fi automat re-afișată** - Totuși dacă postarea este ascunsă de comunitate pentru a doua oară, va rămâne așa până ce va fi gestionată de un membru al echipei -- și asta ar putea atrage și alte consecințe, inclusiv posibilitatea suspendării contului tău.. + Totuși, dacă postarea este ascunsă de comunitate pentru a doua oară, va rămâne așa până ce va fi gestionată de un membru al echipei -- și asta ar putea atrage și alte consecințe, inclusiv posibilitatea suspendării contului tău.. - Pentru îndreptări suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). - usage_tips: - text_body_template: | - Citește câteva sfaturi rapide destinate noilor utilizatori, [urmărind această postare pe blog](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/). - - Pe măsură ce participi aici, vom ajunge să te cunoaștem iar limitările temporare aferente unui utilizator nou, îți vor fi ridicate. Cu timpul vei câștiga [niveluri de încredere](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) care includ abilități speciale și care îți vor da posibilitatea să ne ajuți să ne gestionăm împreuna comunitatea. + Pentru informații suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). welcome_user: subject_template: "Bine ai venit pe %{site_name}!" text_body_template: | @@ -1729,7 +1745,7 @@ ro: Ca măsura de precauție, noului tău cont i s-a blocat posibilitatea de a crea noi răspunsuri sau subiecte până ce un un membru al echipei nu îl va verifica. Ne cerem scuze pentru inconveniență. - Pentru îndreptări suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). + Pentru informații suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). too_many_tl3_flags: subject_template: "Cont nou suspendat" text_body_template: | @@ -1739,7 +1755,7 @@ ro: Ca măsura de precauție, noului tău cont i s-a blocat posibilitatea de a crea noi răspunsuri sau subiecte până ce un un membru al echipei nu îl va verifica. Ne cerem scuze pentru inconveniență. - Pentru îndreptări suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). + Pentru informații suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). blocked_by_staff: subject_template: "Cont suspendat temporar" text_body_template: |+ @@ -1749,7 +1765,7 @@ ro: Poți să continui să răsfoiești, dar nu vei mai putea să răspunzi sau să creezi subiecte până când un [membru al echipei](%{base_url}/about) nu îți verifică postările recente. Ne cerem scuze pentru inconveniență. - Pentru îndreptări suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). + Pentru informații suplimentare, te rugăm să citești [ghidul comunității](%{base_url}/guidelines). user_automatically_blocked: subject_template: "Noul utilizator %{username} a fost blocat de marcajele de avertizare ale comunității." @@ -1975,48 +1991,6 @@ ro: topic_updates: "Actualizări la subiect" view_this_topic: "Vizualizează acest subiect" back_to_top: "Înapoi sus" - forgot_password: - text_body_template: | - Cineva a cerut resetarea parolei tale pe site-ul [%{site_name}](%{base_url}). - - Dacă nu ai cerut-o tu, poți ignora fără griji acest email. - - Click pe următoarea adresă pentru a reseta parola: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Cineva a cerut crearea unei parole pe site-ul [%{site_name}](%{base_url}). Alternativ, te poți autentifica folosind orice serviciu online acceptat (Google, Facebook, etc), serviciu ce este asociat cu acesta adresa de email validată. - - Dacă nu ai cerut asta, poți ignora fără griji acest email. - - Click pe adresa următoare pentru a alege o parolă: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - Cineva a încercat să se autentifice pe contul tău de pe [%{site_name}](%{base_url}). - - Dacă ai fost tu, poți să ignori fără griji acest email. - - Click pe următorul link pentru a te autentifica: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Un cont nou a fost creat pentru tine pe %{site_name} - - Click pe următorul link pentru a-ți alege o parolă pentru noul tău cont - %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - text_body_template: | - Confirmă-ți noua adresă de email pentru %{site_name} dând click pe următorul link: - - %{base_url}/users/authorize-email/%{email_token} - confirm_old_email: - text_body_template: | - Înainte de a-ți putea schimba adresa de email, trebuie să ne confirmi că deții controlul asupra adresei de email curente. După ce completezi acest pas, va trebui să ne confirmi noua adresă de email. - - Confirmă-ți adresa de email curentă pentru %{site_name} dând click pe următorul link: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: text_body_template: | Acesta este un mesaj automat pentru a te informa că adresa ta de email pentru %{site_name} a fost schimbată. Dacă acest lucru s-a întâmplat dintr-o eroare, contractează un administrator al site-lui. @@ -2026,31 +2000,6 @@ ro: %{new_email} signup_after_approval: subject_template: "Contul tă pe site-ul %{site_name} a fost aprobat!" - text_body_template: | - Bun venit pe %{site_name}! - - Un membru al echipei ți-a aprobat contul pe %{site_name}. - - Click link-ul urmator pentru a-ți confirma și activa noul cont: - %{base_url}/users/activate-account/%{email_token} - - Dacă link-ul de mai sus nu este clickabil, încearcă să îl copiezi și să îl lipești în bara de adrese din browser. - - %{new_user_tips} - - Noi credem într-un [comportament civlizat al comunității](%{base_url}/guidelines) indiferent de circumstanțe. - - ședere plăcută! - - (Dacă ai nevoie să comunici cu [membrii echipei](%{base_url}/about) din postura de nou utilizator, pur și simplu dă reply la acest mesaj.) - signup: - text_body_template: | - Bine ai venit pe %{site_name}! - - Click pe următoarea adresă pentru a confirma și activa noul tău cont: - %{base_url}/users/activate-account/%{email_token} - - Dacă adresa de mai sus nu e accesibilă, încearcă să o copiezi și să o lipești în bara de adrese a propriului browser. page_not_found: title: "Oops! Această pagină nu există sau este privată." popular_topics: "Populare" @@ -2537,7 +2486,6 @@ ro: Acest ecuson se acordă prima data când adaugi un emoji la postarea ta :thumbsup:. Emoji permit transmiterea de emoții prin postări, de la fericire :smiley: la tristețe :anguished: la furie :angry: și întreaga planetă :sunglasses: . Apasă un : (două puncte) sau apasă pe butonul Emoji toolbar din editor pentru a alege dintre sute de opțiuni :ok_hand: first_mention: name: Prima mențiune - description: Ai fost menționat în postarea unui utilizator long_description: Acest ecuson se acordă când, pentru prima oară, ai menționat @numele de utilizator al cuiva într-o postare de-a ta. Fiecare menționare generează o notificare către acea persoană, astfel încât ea să știe despre postarea ta. Începi prin a apăsa tasta @ (simbolul at) ca să menționezi orice utilizator sau, dacă îți este permis, grup. E o metodă convenabilă să atragi atenția cuiva. first_onebox: name: Primul onebox @@ -2545,7 +2493,6 @@ ro: long_description: Acest ecuson se acordă când, pentru prima data, ai postat un link singur pe line, ceea ce a făcut ca automat să fie expandat într-un onebox cu un scurt rezumat al link-ului, un titlu și (dacă e disponibilă) cu o poză. first_reply_by_email: name: Forum prin email - description: Ai răspuns la o postare prin email long_description: | Acest ecuosn se acordă când ai răspuns pentru prima dată la o postare prin email :e-mail:. admin_login: @@ -2579,7 +2526,6 @@ ro: safe_mode: title: "Activează modul sigur" description: "Modul sigur îți permite să îți testezi site-ul fără să încarci plugin-uri și personalizări." - no_customizations: "Dezactivează toate personalizările site-ului" only_official: "Dezactivează plugin-urile neoficiale" no_plugins: "Dezactivează toate plugin-urile" enter: "Activează modul sigur" diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 8ae079b2c3..6219c616f8 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -27,7 +27,6 @@ ru: topics: "Темы" posts: "сообщения" loading: "Загрузка..." - powered_by_html: 'При поддержке Discourse, лучше всего использовать с включенным JavaScript' log_in: "Войти" purge_reason: "Деактивированная учетная запись будет автоматически удалена как заброшенная" disable_remote_images_download_reason: "Загрузка картинок была отключена из-за недостаточности места на диске." @@ -327,16 +326,12 @@ ru: attributes: hex: invalid: "не является цветом" - post_reply: - base: - different_topic: "Сообщение и ответ должен принадлежать к одной и той же теме." web_hook: attributes: payload_url: invalid: "URL недействителен. URL должен включает в себя HTTP: // или https: //. И ни один пустой не допускается." <<: *errors user_profile: - no_info_me: "
    Поле Обо мне в вашем профиле не заполнено, не желаете ли что-нибудь написать в нем?
    " no_info_other: "
    %{name} еще не заполнил поле «Обо мне» в своём профайле.
    " vip_category_name: "Фойе" vip_category_description: "Раздел исключительно для пользователей с уровнем доверия 3 и выше." @@ -1269,7 +1264,6 @@ ru: incorrect_username_email_or_password: "Неверное имя пользователя, адрес электронной почты или пароль" wait_approval: "Спасибо за регистрацию. Мы оповестим вас, когда ваша учетная запись будет одобрена." active: "Ваша учетная запись активирована и готова к использованию." - activate_email: "

    Почти готово! Мы выслали письмо на %{email}. Пожалуйста, следуйте инструкциям в этом письме для активации вашей учетной записи.

    Если письмо не пришло, пожалуйста, проверьте папку \"спам\", или попробуйте войти еще раз, чтобы выслать активационное письмо повторно.

    " not_activated: "Вы пока что не можете войти на сайт. Пожалуйста, следуйте инструкциям по активации учетной записи, которые мы отправили вам по электронной почтой." not_allowed_from_ip_address: "Вход с этого IP в качестве пользователя %{username} запрещен." admin_not_allowed_from_ip_address: "Вход с этого IP в качестве администратора запрещен." @@ -1327,13 +1321,6 @@ ru: subject_template: "%{invitee_name} приглашает вас присоединиться к %{site_domain_name}" invite_password_instructions: subject_template: "Задайте пароль для вашей учетной записи на сайте %{site_name}" - text_body_template: | - Спасибо, что приняли приглашение на сайт "%{site_name}" и добро пожаловать! - - Нажмите на следующую ссылку, чтобы установить свой пароль: - %{base_url}/users/password-reset/%{email_token} - - Если срок действия этой ссылки истек, нажмите на кнопку "Войти", а затем "Я забыл свой пароль" и введите ваш e-mail. flag_reasons: off_topic: "Сообщество считает, что ваше сообщение **не относится к теме**, учитывая ее заголовок и первое сообщение." inappropriate: "Сообщество считает ваше сообщение **неприемлемым**, т.е. оскорбительным, непристойным или нурашающим [кодекс чести](/guidelines)." @@ -1345,7 +1332,6 @@ ru: disagreed: "Спасибо за информацию. Уже рассматриваем." deferred: "Спасибо за информацию. Уже рассматриваем." deferred_and_deleted: "Спасибо за информацию. Сообщение удалено." - temporarily_closed_due_to_flags: "Эта тема временно закрыта из-за большого количества жалоб." system_messages: post_hidden: title: "Сообщение Скрыто" @@ -1471,71 +1457,14 @@ ru: back_to_top: "Наверх" forgot_password: title: "Забыл Пароль" - text_body_template: | - Кто-то запросил смену вашего пароля на сайте [%{site_name}](%{base_url}). - - Если это были не вы, спокойно проигнорируйте это письмо. - - Пройдите по следующей ссылке, чтобы задать новый пароль: - %{base_url}/users/password-reset/%{email_token} set_password: title: "Установить Пароль" - text_body_template: | - Кто-то попросил добавить пароль в вашу учётную запись на сайте [%{site_name}](%{base_url}). Вы так же можете войти на сайт используя любой поддерживаемый онлайн-сервис (такие как Google, Facebook и другие), которые связанны с вашим электронным адресом. - - Если вы не делали этот запрос, вы можете просто проигнорировать данное сообщение. - - Нажмите на следующую ссылку для того, что бы изменить пароль: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - Кто-то запросил ссылку для входа в ваш аккаунт [%{site_name}](%{base_url}). - - Если это были не Вы, просто проигнорируйте это сообщение. - - Ссылка для входа: - %{base_url}/users/admin-login/%{email_token} account_created: title: "Учётная Запись Создана" - text_body_template: | - Для вас была создана новая учетная запись на сайте %{site_name} - - Чтобы установить пароль, пройдите по следующей ссылке: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "Подтвердить Новый E-mail" - text_body_template: | - Подтвердите ваш новый адрес электронной почты для сайта %{site_name}, перейдя по следующей ссылке: - - %{base_url}/users/authorize-email/%{email_token} signup_after_approval: subject_template: "Ваша учетная запись на сайте %{site_name} одобрена!" - text_body_template: | - Добро пожаловать на сайт %{site_name}! - - Вы успешно стали участником сообщества %{site_name}! - - Пожалуйста, перейдите по следующей ссылке, чтобы активировать вашу учетную запись: - %{base_url}/users/activate-account/%{email_token} - - Если перейти по ссылке не получается, то просто выделите ссылку, скопируйте ее и вставьте в адресную строку браузера. - - %{new_user_tips} - - - Мы верим в [правила цевилзованного общения](%{base_url}/guidelines). - - Приятного общения! - - (Если вам требуется помощь [администрации](%{base_url}/about), вы можете задать свой вопрос, просто ответив на данное письмо.) - signup: - text_body_template: | - Добро пожаловать на сайт %{site_name}! - - Перейдите по ссылке, чтобы активировать учетную запись: - %{base_url}/users/activate-account/%{email_token} - - Если данная ссылка не работает, попробуйте скопировать её и вставить в строку адреса вашего браузера. page_not_found: title: "Упс! Эта страница не существует или скрыта от публичного просмотра." popular_topics: "Популярные" @@ -1714,7 +1643,6 @@ ru: description: Использовал Эмодзи в Сообщении first_mention: name: Первое обращение - description: Упомянул другого пользователя в сообщении long_description: Эта награда выдается, когда вы впервые вставляете чей-то @псевдоним в свое сообщение. Каждое такое обращение отправит уведомление этому человеку, чтобы они были в курсе вашего сообщения. Чтобы вставить обращение к человеку по его псевдониму, или к целой группе по ее названию (если это разрешено), начните с символа @ - это удобный способ привлечь их внимание. first_onebox: name: Первая Умная Вставка @@ -1748,7 +1676,6 @@ ru: safe_mode: title: "Войти в Безопасный Режим" description: "Безопасный режим позволяет Вам проверить работу Вашего сайта без загрузки плагинов и настроек сайта." - no_customizations: "Отключить все настройки сайта" only_official: "Отключить неофициальные плагины" no_plugins: "Отключить все плагины" enter: "Войти в Безопасный Режим" diff --git a/config/locales/server.sk.yml b/config/locales/server.sk.yml index e63125913c..69b1ed8750 100644 --- a/config/locales/server.sk.yml +++ b/config/locales/server.sk.yml @@ -26,7 +26,6 @@ sk: topics: "Témy" posts: "príspevky" loading: "Načítava sa" - powered_by_html: 'Systém beží na Discourse, najlepšie funguje so zapnutým JavaScriptom' log_in: "Prihlásenie" purge_reason: "Automaticky zmazaný ako opustený, dezaktivovaný účet" disable_remote_images_download_reason: "Sťahovanie vzdialených obrázkov je vypnuté kvôli nedostatku diskového priestoru." @@ -268,12 +267,8 @@ sk: attributes: hex: invalid: "nesprávna farba" - post_reply: - base: - different_topic: "Príspevok a odpoveď musia patriť do rovnakej témy." <<: *errors user_profile: - no_info_me: "
    Položka \"O mne\" vo Vašom profile je prázdna, želáte si ju doplníť?
    " no_info_other: "
    %{name} nevložil zatiaľ nič do profilu \"O mne\"
    " vip_category_name: "Salón" vip_category_description: "Kategória výhradne pre členov so stupňom dôvery 3 a vyšším" @@ -1144,7 +1139,6 @@ sk: incorrect_username_email_or_password: "Nesprávne meno, email, alebo heslo" wait_approval: "Ďakujeme za registráciu. O schválení účtu Vás budeme informovať." active: "Váš účet je aktivovaný a pripravený na používanie." - activate_email: "

    Skoro hotovo! Poslali sme Vám aktivačný email na %{email}. Pre aktiváciu účtu prosíme splňte inštrukcie v emaile.

    Ak nedorazí, skontrolujte si zložku s nevyžiadanou poštou, alebo sa prihláste znovu, aby ste si zaslali ďalší aktivačný email.

    " not_activated: "Systém vás nemôže prihlásiť. Poslali sme vám aktivačný email. Prosím, postupujte podľa inštrukcií na aktiváciu účtu, ktoré sú uvedené v tomto emaile." not_allowed_from_ip_address: "Nie je možné prihlásenie ako %{username} z tejto IP adresy." admin_not_allowed_from_ip_address: "Nie je možné prihlásenie ako admin z tejto IP adresy." @@ -1190,13 +1184,6 @@ sk: subject_template: "%{invitee_name} Vás pozval na %{site_domain_name}" invite_password_instructions: subject_template: "Nastavte heslo pre účet na %{site_name}" - text_body_template: | - Ďakujeme za akceptovanie Vašej pozvánky na %{site_name} -- vitajte! - - Kliknite na odkaz aby ste si vybrali heslo: - %{base_url}/users/password-reset/%{email_token} - - (Ak vyššie uvedený odkaz vyexpiroval, vyberte "Zabudol som heslo" keď sa prihlasujete pomocou svojej emailovej adresy.) flag_reasons: off_topic: "Váš príspevok bol označený ako \"mimo-témy\": komunita cíti, že nie je vhodný do témy tak, ako je definovaná nadpisom a prvým príspevkom" inappropriate: "Váš príspevok bol označený ako **nevhodný**: komunita cíti, že je úrážlivý, nemiestny alebo porušuje [naše pravidlá komunity](/guidelines)." @@ -1208,7 +1195,6 @@ sk: disagreed: "Ďakujeme za upozornenie. Práve to riešime." deferred: "Ďakujeme za upozornenie. Práve to riešime." deferred_and_deleted: "Ďakujeme za upozornenie. Daný príspevok sme odstránili." - temporarily_closed_due_to_flags: "Tento príspevok je dočasne uzamknutý z dôvodu veľkého množstva vlajok od komunity." system_messages: welcome_user: subject_template: "Vitajte na %{site_name}!" @@ -1364,20 +1350,6 @@ sk: why: "Krátky súhrn zo stránky %{site_link} od Vašej poslednej návštevy %{last_seen_at}" since_last_visit: "Od Vašej poslednej návštevy" click_here: "kliknite tu" - forgot_password: - text_body_template: | - Niekto si vyžiadal reset Vášho hesla na [%{site_name}](%{base_url}). - - Ak ste to neboli Vy, môžete ignorovať tento email. - - Pre zmenu hesl kliknite na nasledujúci odkaz - %{base_url}/users/password-reset/%{email_token} - account_created: - text_body_template: | - Nový účet bol pre Vás vytvorený na %{site_name} - - Kliknite na nasledujúci odkaz pre nastavenie hesla k Vášmu novému účtu: - %{base_url}/users/password-reset/%{email_token} signup_after_approval: subject_template: "Váš účet bol schválený na %{site_name}!" page_not_found: @@ -1440,7 +1412,6 @@ sk: badges: first_mention: name: Prvá zmienka - description: Zmienený používateľ v príspevku long_description: Tento odznak je udelený, keď prvýkrát zmienite niekoho @meno vo Vašom príspevku. Každá zmienka generuje upozornenie danej osobe, takže bude vedieť o Vašom príspevku. Jednoducho začnite písať @ (zavináč) aby ste zmienili používateľa alebo, ak je to povolené, skupinu - Je to pohodlný spôsob ako upútať ich pozornosť. admin_login: success: "Email odoslaný" diff --git a/config/locales/server.sq.yml b/config/locales/server.sq.yml index 39ba3d2af7..339010a344 100644 --- a/config/locales/server.sq.yml +++ b/config/locales/server.sq.yml @@ -27,7 +27,6 @@ sq: topics: "Tema" posts: "postime" loading: "Loading" - powered_by_html: 'Mundësuar nga Discourse, për një eksperience më të mirë aktivizoni JavaScript' log_in: "Identifikohu" purge_reason: "Automatically deleted as abandoned, deactivated account" disable_remote_images_download_reason: "Remote images download was disabled because there wasn't enough disk space available." @@ -919,7 +918,6 @@ sq: incorrect_username_email_or_password: "Incorrect username, email or password" wait_approval: "Thanks for signing up. We will notify you when your account has been approved." active: "Your account is activated and ready to use." - activate_email: "

    You're almost done! We sent an activation mail to %{email}. Please follow the instructions in the email to activate your account.

    If it doesn't arrive, check your spam folder, or try to log in again to send another activation mail.

    " not_activated: "You can't log in yet. We sent an activation email to you. Please follow the instructions in the email to activate your account." not_allowed_from_ip_address: "You can't log in as %{username} from that IP address." admin_not_allowed_from_ip_address: "You can't log in as admin from that IP address." @@ -953,14 +951,6 @@ sq: subject_template: "%{invitee_name} ju ka ftuar të bëheni pjesë e %{site_domain_name}" invite_password_instructions: subject_template: "Vendos fjalëkalimin për llogarinë tek %{site_name}" - text_body_template: |+ - Faleminderit që pranuat ftesën tek %{site_name} -- mirësevini! - - Klikoni këtë lidhje për të zgjedhur një fjalëkalim tani: - %{base_url}/users/password-reset/%{email_token} - - (Nëse lidhjes së mësipërme i ka mbaruar afati, zgjidhni "Kam harruar fjalëkalimin" kur të hyni me adresën tuaj email.) - flag_reasons: off_topic: "Your post was flagged as **off-topic**: the community feels it is not a good fit for the topic, as currently defined by the title and the first post." inappropriate: "Your post was flagged as **inappropriate**: the community feels it is offensive, abusive, or a violation of [our community guidelines](/guidelines)." @@ -972,7 +962,6 @@ sq: disagreed: "Thanks for letting us know. We're looking into it." deferred: "Thanks for letting us know. We're looking into it." deferred_and_deleted: "Thanks for letting us know. We've removed the post." - temporarily_closed_due_to_flags: "Kjo temë është përkohësisht e mbyllur për shkak të një numri të madh sinjalizimesh nga komuniteti." system_messages: post_hidden: subject_template: "Postimi u fsheh ngaqë u sinjalizua nga komuniteti" @@ -1214,50 +1203,6 @@ sq: topic_updates: "Tema të azhornuara" view_this_topic: "Shiko këtë temë" back_to_top: "Kthehu sipër" - forgot_password: - text_body_template: | - Dikush kërkoi për të rivendosur fjalëkalimin tuaj tek [%{site_name}](%{base_url}). - - Nëse nuk ishit ju, mund të mos e merrni parasysh këtë email pa asnjë problem. - - Klikoni lidhjen e mëtejshme për të zgjedhur një fjalëkalim të ri: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Dikush kërkoi për të shtuar një fjalëkalim tek [%{site_name}](%{base_url}). Nga ana tjetër, mund të hyni në sistem duke përdorur një nga shërbimet online që mbështeten (Google, Facebook, etj) i cili është i lidhur me këtë adresë email. - - Nëse nuk e bëtë ju këtë kërkesë, mund të mos e merrni parasysh këtë email pa asnjë problem. - - Klikoni lidhjen e mëtejshme për të zgjedhur një fjalëkalim: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - Somebody asked to login to your account on [%{site_name}](%{base_url}). - - If you did not make this request, you can safely ignore this email. - - Click the following link to login: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Një llogari e re u krijua për ju tek %{site_name} - - Klikoni lidhjen e mëtejshme për të zgjedhur një fjalëkalim për llogarinë tuaj të re: - %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - text_body_template: | - Konfirmo adresën e re email për faqen %{site_name} duke klikuar në linkun më poshtë: - - %{base_url}/users/authorize-email/%{email_token} - confirm_old_email: - text_body_template: | - Përpara se sa ta ndryshoni adresën tuaj email, nevojitet të konfirmoni që jeni i - zoti i saj. Pasi ta përmbushni këtë hap, ne - do të miratojmë adresën tuaj të re email. - - Konfirmoni adresën tuaj të tanishme email për %{site_name} duke klikuar lidhjen e mëtejshme: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: text_body_template: | Ky është një mesazh automatik që ju njofton se adresa juaj email për @@ -1268,31 +1213,6 @@ sq: %{new_email} signup_after_approval: subject_template: "Jeni aprovuar tek %{site_name}!" - text_body_template: | - Mirësevini tek %{site_name}! - - Një anëtar stafi miratoi llogarinë tuaj në %{site_name}. - - Klikoni lidhjen e mëtejshme për të konfirmuar dhe aktivizuar llogarinë tuaj të re: - %{base_url}/users/activate-account/%{email_token} - - Nëse lidhja e mësipërme nuk punon, mundohuni ta kopjoni dhe hapni atë në shfletuesin tuaj web. - - %{new_user_tips} - - Ne besojmë në [sjelljen e qytetëruar të komunitetit](%{base_url}/guidelines) kurdoherë. - - Shijojeni qëndrimin tuaj! - - (Nëse ju duhet të komunikoni me [anëtarët e stafit](%{base_url}/about) si një anëtar i ri, thjesht përgjigjuni këtij mesazhi.) - signup: - text_body_template: | - Mirësevini tek %{site_name}! - - Klikoni lidhjen e mëtejshme dhe aktivizoni llogarinë tuaj të re: - %{base_url}/users/activate-account/%{email_token} - - Nëse lidhja e mësipërme nuk punon, mundohuni ta kopjoni dhe hapni atë në shfletuesin tuaj web. page_not_found: popular_topics: "Popullore" recent_topics: "Më të fundit" @@ -1689,7 +1609,6 @@ sq: Kjo stemë jepet kur ju shtoni një Emoji në postimin tuaj për herë të parë :thumbsup:. Emoji ju lejon të përçoni emocionet në postimet tuaja, prej kënaqësisë :smiley: tek mërzitja :anguished: e deri tek inati :angry: dhe çdo gjë midis :sunglasses:. Thjesht filloni të shkruani : (dy pika) ose shtypni butonin Emoji në shiritin e veglave të fushës së shkrimit për të zgjedhur mes qindra llojesh :ok_hand: first_mention: name: Përmendja e parë - description: Përmendi një përdorues në një postim long_description: Kjo stemë jepet herën e parë kur ju përmendni @username e dikujt në një postim. Secila përmendje krijon një njoftim për atë person, që të vihen në dijeni për postimin tuaj. Thjesht filloni të shtypni @ (simbolin te) për të përmendur çdo përdorues ose, nëse e mundur, çdo grup - është një mënyrë e volitshme për të patur vëmendjen e dikujt. first_onebox: name: Onebox i Parë @@ -1697,7 +1616,6 @@ sq: long_description: Kjo stemë jepet herën e parë kur postoni një lidhje në një rresht të vetëm, i cili shpaloset automatikisht në një onebox me një përmbledhje të shkurtër të lidhjes, një titull, dhe (kur është e mundur) një imazh. first_reply_by_email: name: Përgjigja e parë me email - description: Iu përgjigj një postimi me email long_description: | Kjo stemë jepet herën e parë kur i përgjigjeni një postimi nëpërmjet email-it :e-mail: admin_login: diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index 825960a14d..e192c2047c 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -27,7 +27,6 @@ sv: topics: "Ämnen" posts: "inlägg" loading: "Laddar" - powered_by_html: 'Drivs av Discourse, visas bäst med JavaScript aktiverat' log_in: "Logga in" purge_reason: "Automatiskt borttaget som övergett, avaktiverat konto" disable_remote_images_download_reason: "Nedladdning av externa bilder är inaktiverat eftersom det inte fanns tillräckligt mycket lagringsutrymme tillgängligt." @@ -281,14 +280,13 @@ sv: invalid: "är inte en giltig färg" post_reply: base: - different_topic: "Inlägg och svar måste tillhöra samma ämne." + different_topic: "Inlägg och svar måste höra till samma ämne." web_hook: attributes: payload_url: invalid: "Webbadressen är ogiltig. Webbadressen skall inkludera :// eller https://. Inga blanktecken är tillåtna" <<: *errors user_profile: - no_info_me: "
    Din profils Om Mig-fält är för närvarande tomt, skulle du vilja fylla i det?
    " no_info_other: "
    %{name} har inte skrivit någonting i dess profils Om Mig-fält ännu
    " vip_category_name: "Lounge" vip_category_description: "En kategori exklusiv för medlemmar med en förtroendenivå av 3 eller högre." @@ -1302,7 +1300,6 @@ sv: incorrect_username_email_or_password: "Felaktigt användarnamn, e-post eller lösenord" wait_approval: "Tack för din registrering. Vi meddelar dig när ditt konto blivit godkänt." active: "Ditt konto är aktiverat och redo att användas." - activate_email: "

    Du är nästan klar! Vi skickade ett e-post med aktivering till %{email}. Var god följ instruktionerna i e-postmeddelandet för att aktivera ditt konto.

    Om du inte mottar e-posten, kontrollera skräpposten eller försök logga in igen för att skicka ett nytt aktiveringsbrev.

    " not_activated: "Du kan inte logga in ännu. Vi har skickat ett aktiveringsmejl till dig. Vänligen följ instruktionerna i mejlet för att aktivera ditt konto." not_allowed_from_ip_address: "Du kan inte logga in som %{username} från den IP-adressen." admin_not_allowed_from_ip_address: "Du kan inte logga in som admin från den IP-adressen." @@ -1360,18 +1357,10 @@ sv: title: "Invitera forumets skrivare" subject_template: "%{invitee_name} har bjudit in dig till %{site_domain_name}" custom_invite_forum_mailer: - title: "Invitera forumets skrivare" subject_template: "%{invitee_name} har bjudit in dig till %{site_domain_name}" invite_password_instructions: title: "Invitera lösenords instruktioner" subject_template: "Ange lösenord för ditt konto på %{site_name}" - text_body_template: | - Tack för att du har accepterat inbjudan till %{site_name} -- välkommen hit! - - Klicka på länken nedanför för att bestämma ett nytt lösenord nu: - %{base_url}/users/password-reset/%{email_token} - - (Om länken ovanför har löpt ut, klicka på "jag har glömt mitt lösenord" när du loggar in med din e-postadress). test_mailer: title: "test mail skrivare" new_version_mailer: @@ -1395,7 +1384,6 @@ sv: disagreed: "Tack för att du meddelande oss. Vi undersöker det." deferred: "Tack för att du meddelande oss. Vi undersöker det." deferred_and_deleted: "Tack för att du meddelande oss. Vi har raderat inlägget." - temporarily_closed_due_to_flags: "Detta ämne är tillfälligt stängt på grund av ett stort antal flaggor." system_messages: post_hidden: title: "Gömda inlägg" @@ -1859,52 +1847,16 @@ sv: back_to_top: "Tillbaka till början" forgot_password: title: "Glömt lösenord" - text_body_template: | - Någon bad om att få återställa ditt lösenord på [%{site_name}](%{base_url}). - - Om detta inte var du, kan du utan problem ignorera detta mejl. - - Klicka på följande länk för att välja ett nytt lösenord: - %{base_url}/users/password-reset/%{email_token} set_password: title: "Välj lösenord" - text_body_template: | - Någon har begärt att lägga till ett lösenord till ditt konto på [%{site_name}](%{base_url}). Du kan också logga in genom att använda någon av våra alternativa onlinetjänster (Google eller Facebook) som är kopplat till din validerade e-postadress. - - Om det inte var på din begäran så kan du utan problem ignorera det här e-postmeddelandet. - - Följ nedanstående länk för att välja ett nytt lösenord: - %{base_url}/users/password-reset/%{email_token} admin_login: title: "Administratörs login" - text_body_template: | - Någon bad om att få återställa ditt lösenord på [%{site_name}](%{base_url}). - - Om det inte var på din begäran kan du utan problem ignorera detta mejl. - - Klicka på följande länk för att logga in: - %{base_url}/users/password-reset/%{email_token} account_created: title: "Konto skapat" - text_body_template: | - Ett nytt konto har skapats för dig på %{site_name} - - Klicka på följande länk för att välja ett nytt lösenord för ditt nya konto: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "Bekräfta den nya emailadressen" - text_body_template: | - Bekräfta din nya e-postadress för %{site_name} genom att klicka på länken nedanför: - - %{base_url}/users/authorize-email/%{email_token} confirm_old_email: title: "Bekräfta den gamla email adressen" - text_body_template: | - Vänligen bekräfta att du kontrollerar den här e-postadressen innan vi ändrar din e-postadress. - - Du kan bekräfta e-postadressen för %{site_name} genom att följa länken nedan: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: title: "Meddela gamla epostmeddelanden" text_body_template: | @@ -1916,32 +1868,8 @@ sv: signup_after_approval: title: "Bli medlem efter godkännande" subject_template: "Du har blivit godkänd på %{site_name}!" - text_body_template: | - Välkommen till %{site_name}! - - En administratör har godkänt ditt konto på %{site_name}. - - Klicka på följande länk för att bekräfta och aktivera ditt nya konto: - %{base_url}/users/activate-account/%{email_token} - - Om länken ovan inte går att klicka på kan du kopiera och klistra in länken i adressfältet i din webbläsare. - - %{new_user_tips} - - Vi tror på ett [civiliserat beteende](%{base_url}/guidelines) vid alla tillfällen. - - Njut av din vistelse! - - (Om du som ny användare behöver komma i kontakt med vår [personal](%{base_url}/about), svara bara på detta meddelande.) signup: title: "Bli medlem" - text_body_template: | - Välkommen till %{site_name}! - - Klicka på följande länk för att bekräfta och aktivera ditt nya konto: - %{base_url}/users/activate-account/%{email_token} - - Om länken ovan inte går att klicka på kan du kopiera och klistra in länken i adressfältet i din webbläsare. page_not_found: title: "Hoppsan! Den sidan finns inte eller så är den privat." popular_topics: "Populära" @@ -2212,7 +2140,6 @@ sv: Den här utmärkelsen beviljas när du för första gången använder en emoji i ditt inlägg :thumbsup:. Emojis låter dig förmedla dina känslor i dina inlägg, från glädje :smiley: till sorgsenhet :anguished: till ilska :angry: och allt däremellan :sunglasses:. Skriv bara ett : (kolon) eller klicka på emoji-knappen i redigeraren för att välja mellan hundratals emojis :ok_hand: first_mention: name: Första omnämningen - description: Omnämnde en användare i ett inlägg long_description: 'Den här utmärkelsen beviljas när du nämner någons @användarnamn i ett inlägg. Varje omnämnande genererar en notifiering till den personen, så att det känner till ditt inlägg. Börja skriva @ (snabel-a) för att nämna en användare, eller, om det tillåts, en grupp - det är ett bekvämt sätt att uppmärksamma dem på något. ' first_onebox: name: Första onebox:en @@ -2220,7 +2147,6 @@ sv: long_description: 'Den här utmärkelsen beviljas när du för första gången lägger upp en länk på en rad för sig själv, vilken sedan automatiskt utvidgas och blir en onebox med en kort sammanfattning av länken, en rubrik och (när det finns tillgängligt) även en bild. ' first_reply_by_email: name: Första svaret via e-post - description: Svarade på ett inlägg via e-post long_description: | Den här utmärkelsen beviljas när du för första gången svarar på ett inlägg via e-post :e-mail:. admin_login: @@ -2251,7 +2177,6 @@ sv: safe_mode: title: "ange felsäkert läge" description: "I felsäkert läge kan du testa din webplats utan att ladda ner några plugins eller anpassade webplatser." - no_customizations: "Inaktivera alla webplats inställningar" only_official: "Inaktivera alla inofficiella plugins" no_plugins: "Inaktivera alla plugins" enter: "Starta felsäkert läge" diff --git a/config/locales/server.te.yml b/config/locales/server.te.yml index 995795f619..5d6a8fe2d4 100644 --- a/config/locales/server.te.yml +++ b/config/locales/server.te.yml @@ -14,7 +14,6 @@ te: topics: "విషయాలు" posts: "టపాలు" loading: "లోడవుతోంది" - powered_by_html: ' డిస్కౌర్స్ చేత శక్తివంతం చేయబడింది, జావాస్క్రిప్ట్ చేతనం చేస్తే బాగా కనిపిస్తుంది. ' log_in: "లాగిన్" disable_remote_images_download_reason: "సుదూర బొమ్మల దిగుమతి అచేతనమైంది ఎందుకంటే డిస్క్ జాగా తక్కువగా ఉంది." errors: &errors diff --git a/config/locales/server.tr_TR.yml b/config/locales/server.tr_TR.yml index 5f02044348..6ea92de572 100644 --- a/config/locales/server.tr_TR.yml +++ b/config/locales/server.tr_TR.yml @@ -27,7 +27,6 @@ tr_TR: topics: "Konular" posts: "gönderiler" loading: "Yükleniyor" - powered_by_html: 'Gücünü Discourse''tan alır, en iyi görünüm için JavaScript etkinleştirmeniz gerekir' log_in: "Giriş Yap" purge_reason: "terkedilmiş, edilgen hesap olarak otomatik olarak silindi" disable_remote_images_download_reason: "Yeterli disk alanı kalmaması sebebiyle uzaktan görüntü indirme devre dışı bırakıldı." @@ -237,12 +236,8 @@ tr_TR: attributes: hex: invalid: "geçerli bir renk değil" - post_reply: - base: - different_topic: "Gönderi ve cevap aynı konuya ait olmak zorunda." <<: *errors user_profile: - no_info_me: "
    Profilinizdeki Hakkımda bölümü boş, doldurmak ister misiniz?
    " no_info_other: "
    %{name} profilinde Hakkımda kısmına henüz bir şey girmedi
    " vip_category_name: "Lobi" vip_category_description: "Güven seviyesi 3 ve üzerinde olan üyelere özel kategori." @@ -1117,7 +1112,6 @@ tr_TR: incorrect_username_email_or_password: "Yanlış kullanıcı adı, e-posta ya da parola" wait_approval: "Kayıt olduğunuz için teşekkürler. Hesabınız onaylandığında sizi haberdar edeceğiz." active: "Hesabınız etkinleştirildi ve kullanıma hazır." - activate_email: "

    Az kaldı! %{email} adresine bir etkinleştirme e-postası gönderdik. Hesabınızı etkinleştirmek için lütfen e-postadaki yönlendirmeleri takip edin.

    Eğer e-postayı almadıysanız, gereksiz klasörünüzü kontrol edin ya da başka bir etkinleştirme e-postası daha göndermek için tekrar giriş yapmayı deneyin.

    " not_activated: "Henüz giriş yapamazsınız. Size bir etkinleştirme e-postası gönderdik. Hesabınızı etkinleştirmek için lütfen e-postadaki yönlendirmeleri takip edin." not_allowed_from_ip_address: "Bu IP adresinden %{username} kullanıcı adı ile giriş yapamazsınız." admin_not_allowed_from_ip_address: "Bu IP adresinden admin olarak giriş yapamazsınız." @@ -1169,13 +1163,6 @@ tr_TR: subject_template: "%{invitee_name} sizi %{site_domain_name} sitesine katılmaya davet etti" invite_password_instructions: subject_template: "%{site_name} hesabınız için parola oluşturun" - text_body_template: | - %{site_name} topluluğuna olan davetiyeni kabul ettiğin için teşekkür ederiz -- hoş geldin! - - Şimdi bir parola seçmek için şu bağlantıya tıklayın: - %{base_url}/users/password-reset/%{email_token} - - (Eğer yukarıdaki bağlantının süresi dolmuşsa e-posta adresinizle giriş yaparken "Parolamı unuttum" bağlantısına tıklayınız.) new_version_mailer: text_body_template: | Yuhaaa, [Discourse](http://www.discourse.org) için yeni bir sürüm hazır! @@ -1215,7 +1202,6 @@ tr_TR: disagreed: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Konu ile ilgileniyoruz." deferred: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Konu ile ilgileniyoruz." deferred_and_deleted: "Bizi bilgilendirdiğiniz için teşekkür ederiz. Gönderiyi kaldırdık." - temporarily_closed_due_to_flags: "Bu konu topluluk tarafından çok fazla bildirildiği için geçici olarak kapatılmıştır." system_messages: post_hidden: subject_template: "Gönderi topluluk bildirimleri tarafından gizlendi" @@ -1519,47 +1505,6 @@ tr_TR: topic_updates: "Konu güncellemeleri" view_this_topic: "Bu konuyu görüntüle" back_to_top: "Başa dön" - forgot_password: - text_body_template: | - [%{site_name}](%{base_url}) sitesinde, biri parolanızın sıfırlanması için talepte bulundu. - - Eğer talepte bulunan siz değilseniz, bu e-postayı görmezden gelebilirsiniz. - - Yeni parola oluşturmak için aşağıdaki bağlantıya tıklayın: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Biri, [%{site_name}](%{base_url}) sitesindeki hesabınıza parola ekleme talebinde bulundu. Dilerseniz, bu doğrulanmış e-posta hesabınızla eşleşen, desteklediğiniz herhangi bir çevrimiçi servisi (Google, Facebook, vs.) kullanarak da giriş yapabilirsiniz. - - Eğer istekte bulunan siz değilseniz, bu e-postayı görmezden gelebilirsiniz. - - Parola oluşturmak için aşağıdaki bağlantıya tıklayın: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - [%{site_name}](%{base_url}) sitesinde, biri hesabınıza giriş yapmaya talebinde bulundu. - - Eğer talepte bulunan siz değilseniz, bu e-postayı görmezden gelebilirsiniz. - - Giriş yapmak için aşağıdaki bağlantıya tıklayın: - %{base_url}/users/password-reset/%{email_token} - account_created: - text_body_template: | - %{site_name} sitesinde sizin için yeni bir hesap oluşturuldu. - - Yeni hesabınıza ait bir parola oluşturmak için aşağıdaki bağlantıya tıklayın: - %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - text_body_template: | - %{site_name} adresindeki yeni e-posta adresinizi aşağıdaki bağlantıya tıklayarak doğrulayın - %{base_url}/users/authorize-email/%{email_token} - confirm_old_email: - text_body_template: | - E-posta adresinizi değiştirmeden önce, şu anki e-posta adresinizin kontrolünün sizde olduğunu doğrulamamız gerekiyor. Bu adımı tamamlandıktan sonra, yeni e-posta adresinizi doğrulayacaksınız. - - %{site_name} sitesindeki şu anki e-posta adresinizi aşağıdaki bağlantıya tıklayıp doğrulayın: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: text_body_template: | Bu %{site_name} forumunda kullandığınız e-posta adresinizin değiştiğini @@ -1571,30 +1516,6 @@ tr_TR: %{new_email} signup_after_approval: subject_template: "%{site_name} sitesinde hesabınız onaylandı!" - text_body_template: | - %{site_name} topluluğuna hoş geldiniz! - - %{site_name} sitesindeki hesabınız bir görevli tarafından onaylandı. - - Aşağıdaki linke tıklayın ve yeni hesabınızı aktive edin: %{base_url}/users/activate-account/%{email_token} - - Eğer link tıklanabilir değilse, linki kopyalayıp tarayıcınızın adres çubuğuna yapıştırmayı deneyebilirsiniz. - - %{new_user_tips} - - Biz her zaman [topluluk yönergelerimize](%{base_url}/guidelines) inanıyoruz. - - Ziyaretinizin keyfini çıkarın! - - (Eğer yeni bir kullanıcı olarak [görevlilerle](%{base_url}/about) iletişim kurmak isterseniz, bu iletiyi cevaplamanız yeterli.) - signup: - text_body_template: | - %{site_name} topluluğuna hoş geldiniz! - - Hesabınızı onaylamak ve etkinleştirmek için aşağıdaki bağlantıya tıklayın: - %{base_url}/users/activate-account/%{email_token} - - Eğer yukarıdaki bağlantı tıklanabilir değilse, bağlantıyı kopyalayıp tarayıcınızın adres çubuğuna yapıştırmayı deneyin. page_not_found: title: "Hay aksi! Bu sayfa yok ya da gizli." popular_topics: "Gözde" @@ -1859,14 +1780,12 @@ tr_TR: Bu rozet gönderinize ilk defa bir emoji eklediğiniz için verilmiştir :thumbsup:. first_mention: name: İlk Bahsetme - description: 'Gönderide bir kullanıcıdan bahsetme ' long_description: Bu rozet ilk defa gönderinizde başka bir kullanıcıdan bahsettiğiniz için verilmiştir. Her bahsetme bahsi geçen kullanıcıya bildirim gitmesini sağlar, böylece bu kullanıcı sizin gönderinizden haberdar olur. Sadece bahsi geçen kullanıcı adına @ gerekir. first_onebox: name: İlk Kutulama description: Kutulanmış bir bağlantı gönderdi first_reply_by_email: name: Mail İle İlk Cevap - description: 'Gönderiye mail yoluyla cevap verme ' long_description: | Bu rozet ilk defa bir gönderiye mail yoluyla cevap verdiğiniz için verilmiştir :e-mail:. admin_login: @@ -1897,7 +1816,6 @@ tr_TR: message: "

    %{email} adresine etkinleştirme e-postasını tekrar gönderdik" safe_mode: title: "Güvenli moda gir" - no_customizations: "Tüm site özelleştirmelerini devre dışı bırak" no_plugins: "Tüm eklentileri devre dışı bırak" enter: "Güvenli Moda Gir" wizard: diff --git a/config/locales/server.uk.yml b/config/locales/server.uk.yml index 20c0e16d37..4210ce8eb8 100644 --- a/config/locales/server.uk.yml +++ b/config/locales/server.uk.yml @@ -14,7 +14,6 @@ uk: topics: "Теми" posts: "дописи" loading: "Завантаження" - powered_by_html: 'Створено за допомогою технології Discourse, бажано переглядати з увімкненим JavaScript' log_in: "Вхід" emails: incoming: @@ -130,7 +129,6 @@ uk: invalid: "Хибний URL. URL має містити http:// або https://. І пробіли не дозволяються." <<: *errors user_profile: - no_info_me: "

    поле Про Мене вашого профілю зараз є порожнім, ви маєте бажання заповнити його?
    " no_info_other: "
    %{name} hasn't entered anything in the About Me field of their profile yet
    " vip_category_description: "A category exclusive to members with trust level 3 and higher." staff_category_name: "Персонал" @@ -427,32 +425,8 @@ uk: posted_by: "Опубліковано %{username} %{post_date}" digest: click_here: "натисніть тут" - forgot_password: - text_body_template: | - Хтось попросив скинути Ваш пароль на сайті [%{site_name}](%{base_url}). - - Якщо це були не Ви, можете спокійно проігнорувати цей лист. - - Перейдіть за посиланням, щоб обрати новий пароль: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Хтось попросив додати пароль до Вашого облікового запису на сайті [%{site_name}](%{base_url}). Замість цього, Ви можете входити в систему, використовуючи будь-який із підтримуваних онлайн-сервісів (Google, Facebook, і т. ін.), який поєднаний із цією перевіреною електронною скринькою. - - Якщо Ви не робили такого запиту, можете спокійно проігнорувати цей лист. - - Перейдіть за посиланням, щоб обрати пароль: - %{base_url}/users/password-reset/%{email_token} signup_after_approval: subject_template: "Вас було схвалено на сайті %{site_name}!" - signup: - text_body_template: | - Вітаємо у %{site_name}! - - Натисніть це посилання для підтвердження та активації Вашого нового облікового запису: - %{base_url}/users/activate-account/%{email_token} - - Якщо наведене посилання не є клікабельним, спробуйте скопіювати та вставити його в адресний рядок Вашого веб-оглядача. page_not_found: popular_topics: "Популярні" recent_topics: "Останні" diff --git a/config/locales/server.vi.yml b/config/locales/server.vi.yml index 5789578a5c..e7eb9ef580 100644 --- a/config/locales/server.vi.yml +++ b/config/locales/server.vi.yml @@ -26,7 +26,6 @@ vi: topics: "Chủ đề" posts: "bài viết" loading: "Đang tải" - powered_by_html: 'Được hỗ trợ bởi Discourse, xem tốt nhất khi JavaScript được kích hoạt' log_in: "Đăng nhập" disable_remote_images_download_reason: "Không thể tải ảnh về máy chủ vì thiếu dung lượng." anonymous: "Ẩn danh" @@ -240,7 +239,6 @@ vi: invalid: "không phải là một màu không hợp lệ" <<: *errors user_profile: - no_info_me: "
    Mục nói về bản thân bạn trong hồ sơ của bạn hiện đang trống, bạn muốn điền vào nó? " no_info_other: "
    %{name} đã không nhập bất cứ điều gì nói về bản thân của họ " vip_category_name: "Phòng khách" vip_category_description: "Một chuyên mục chỉ dành cho thành viên có mức tin tưởng 3 hoặc cao hơn" @@ -1065,7 +1063,6 @@ vi: incorrect_username_email_or_password: "Không đúng tài khoản, email hoặc mật khẩu" wait_approval: "Cảm ơn bạn đã đăng ký. Chúng tôi sẽ thông báo sau khi tài khoản của bạn được kiểm duyệt." active: "Tài khoản của bạn đã được kích hoạt và sẵn sàng để sử dụng." - activate_email: "

    Bạn đã gần hoàn thành! Chúng tôi đã gửi bạn một email kích hoạt tới địa chỉ %{email}. Xin hãy làm theo hướng dẫn trong email đó để kích hoạt tài khoản của bạn.

    Nếu bạn không nhận được thư, hãy kiểm tra thư mục thư rác, hoặc thử đăng nhập lại để được nhận email kích hoạt khác.

    " not_activated: "Bạn không thể đăng nhập bây giờ. Chúng tôi đã gửi bạn một email kích hoạt tài khoản. Vui lòng làm theo hướng dẫn trong email để kích hoạt tài khoản của bạn." not_allowed_from_ip_address: "Bạn không thể đăng nhập như là %{username} từ địa chỉ IP này." admin_not_allowed_from_ip_address: "Bạn không thể đăng nhập như quản trị từ IP này." @@ -1114,13 +1111,6 @@ vi: subject_template: "%{invitee_name} đã mời bạn gia nhập %{site_domain_name}" invite_password_instructions: subject_template: "Đặt mật khẩu cho tài khoản của bạn ở %{site_name}" - text_body_template: | - Cám ơn bạn đã chấp nhận lời mời tham gia %{site_name} -- chào mừng! - - Xin hãy click liên kết sau để đặt mật khẩu: - %{base_url}/users/password-reset/%{email_token} - - (Nếu liên kết trên hết hạn, bạn hãy chọn "Quên mật khẩu" khi đăng nhập với email của bạn). flag_reasons: off_topic: "Bài viết của bạn được đánh dấu là **off-topic**: cộng đồng cảm thấy nó không phù hợp cho chủ đề này, được xác định bởi tiêu đề và bài viết đầu tiên." inappropriate: "Bài viết của bạn được đánh dấu là **inappropriate**: cộng đồng cảm thấy đó là sự xúc phạm, lăng mạ, hoặc vi phạm [nội quy diễn đàn](/guidelines)." @@ -1132,7 +1122,6 @@ vi: disagreed: "Cảm ơn đã cho chúng tôi biết thông tin. Chúng tôi đang xem xét nó." deferred: "Cảm ơn đã cho chúng tôi biết thông tin. Chúng tôi đang xem xét nó." deferred_and_deleted: "Cảm ơn bạn đã cho chúng tôi biết, chúng tôi đã gỡ bỏ bài viết." - temporarily_closed_due_to_flags: "Chủ đề này tạm thời đóng do có một lượng lớn đánh dấu từ cộng đồng." system_messages: welcome_user: subject_template: "Chào mừng đến với %{site_name}!" @@ -1353,48 +1342,6 @@ vi: new_topics: "Chủ đề mới" view_this_topic: "Xem chủ đề này" back_to_top: "Trở lên trên" - forgot_password: - text_body_template: | - Có người đã yêu cầu được đặt lại mật khẩu của bạn trên [%{site_name}](%{base_url}). - - Nếu bạn không yêu cầu điều này, hãy bỏ qua email này. - - Click vào liên kết sau để chọn mật khẩu mới: - %{base_url}/users/password-reset/%{email_token} - set_password: - text_body_template: | - Có người đã yêu cầu thêm mật khẩu vào tài khoản của bạn trên [%{site_name}](%{base_url}). Ngoài ra, bạn có thể đăng nhập sử dụng bất kỳ dịch vụ trực tuyến nào hỗ trợ (Google, Facebook,...) được liên kết với địa chỉ email này. - - Nếu bạn không yêu cầu điều này, hãy bỏ qua email này. - - Click vào liên kết sau để chọn mật khẩu mới: - %{base_url}/users/password-reset/%{email_token} - admin_login: - text_body_template: | - Có người đã yêu cầu đăng nhập vào tài khoản của bạn trên [%{site_name}](%{base_url}). - - Nếu bạn không yêu cầu điều này, hãy bỏ qua email này. - - Click liên kết sau để đăng nhập: - %{base_url}/users/admin-login/%{email_token} - account_created: - text_body_template: | - Tài khoản mới cho bạn trên %{site_name} đã được tạo - - Click liên kết sau để chọn mật khẩu cho tài khoản của bạn: - %{base_url}/users/password-reset/%{email_token} - confirm_new_email: - text_body_template: | - Xác nhận địa chỉ email mới của bạn trên %{site_name} bằng cách click liên kết sau: - - %{base_url}/users/authorize-email/%{email_token} - confirm_old_email: - text_body_template: | - Trước khi thay đổi địa chỉ email của bạn, chúng tôi cần xác nhận rằng bạn kiểm soát địa chỉ email của tài khoản hiện tại. Sau khi hoàn thành bước này, bạn có thể xác nhận địa chỉ email mới. - - Xác nhận địa chỉ email hiện tại của bạn trên %{site_name} bằng cách click liên kết sau: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: text_body_template: | Đây là tin nhắn tự động để thông báo rằng địa chỉ email của bạn trên %{site_name} đã được thay đổi. Nếu điều này là không đúng, xin hãy liên hệ với BQT. @@ -1404,31 +1351,6 @@ vi: %{new_email} signup_after_approval: subject_template: "Bạn đã được kiểm duyệt ở %{site_name}!" - text_body_template: | - Chào mừng bạn tham gia %{site_name}! - - BQT đã chấp nhận tài khoản của bạn trên %{site_name}. - - Click liên kết sau để xác nhận và kích hoạt tài khoản của bạn: - %{base_url}/users/activate-account/%{email_token} - - Nếu bạn không thể click liên kết trên, hãy thử sao chép và dán vào thanh địa chỉ của trình duyệt web. - - %{new_user_tips} - - Chúng tôi tin rằng bạn sẽ cùng chúng tôi hướng tới một cộng đồng văn minh [quy tắc ứng xử văn minh](%{base_url}/guidelines). - - Chúc vui vẻ! - - (Nếu bạn cần liên hệ với BQT [nhân viên quản trị](%{base_url}/about), bạn có thể gửi trả lời tin nhắn này). - signup: - text_body_template: | - Chào mừng bạn tham gia %{site_name}! - - Click liên kết sau để xác nhận và kích hoạt tài khoản của bạn: - %{base_url}/users/activate-account/%{email_token} - - Nếu bạn không thể click liên kết trên, hãy thử sao chép và dán vào thanh địa chỉ của trình duyệt web. page_not_found: title: "Rất tiếc! Địa chỉ này không tồn tại hoặc riêng tư." popular_topics: "Phổ biến" @@ -1678,7 +1600,6 @@ vi: description: Sử dụng Emoji trong bài viết first_mention: name: Đề cập đầu tiên - description: Đề cập một thành viên trong bài viết admin_login: success: "Gửi mail lỗi" error: "Lỗi!" diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 0b3a09841d..21219b2cbe 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -27,7 +27,6 @@ zh_CN: topics: "主题列表" posts: "帖子" loading: "载入中" - powered_by_html: 'Powered by
    Discourse,启用JavaScript获致最佳浏览' log_in: "登录" purge_reason: "自动删除废弃,停用账户" disable_remote_images_download_reason: "磁盘空间不足,远程图像下载已经被停用。" @@ -302,16 +301,12 @@ zh_CN: attributes: hex: invalid: "不是有效颜色值" - post_reply: - base: - different_topic: "帖子和回复必须属于同一主题." web_hook: attributes: payload_url: invalid: "URL 不合法。URL 应包括 http:// 或 https://。并且其中没有空格。" <<: *errors user_profile: - no_info_me: "
    你的资料中,关于我部分目前还是空白,你想要写些什么吗?
    " no_info_other: "
    %{name} 尚未在他们的资料中的关于我部分填写任何信息
    " vip_category_name: "贵宾室" vip_category_description: "信任等级3的用户参与讨论的分类。" @@ -1328,7 +1323,6 @@ zh_CN: incorrect_username_email_or_password: "用户名、电子邮箱或密码不正确" wait_approval: "谢谢注册帐号。我们会在你的帐号获得批准之后通知你。" active: "你的帐号已经被激活,可以使用了。" - activate_email: "

    快完成了!我们发送了一封激活邮件到%{email}。请按照邮件中的步骤来激活你的帐号。

    如果你没有收到邮件,请检查你的垃圾邮件收件箱,或者试试再登录一次,看看能不能收到另一封激活邮件。

    " not_activated: "你还不能登录。我们发送了一封激活邮件给你,请按照邮件中的步骤来激活你的帐号。" not_allowed_from_ip_address: "你不能以 %{username} 身份从该 IP 地址登陆。" admin_not_allowed_from_ip_address: "你不能从该 IP 地址以管理员身份登录。" @@ -1388,13 +1382,6 @@ zh_CN: subject_template: "%{invitee_name}邀请你加入%{site_domain_name}" invite_password_instructions: subject_template: "为 %{site_name} 账户设置密码" - text_body_template: | - 感谢你接受来自%{site_name}的邀请——欢迎! - - 点击下面的链接立即选择一个密码: - %{base_url}/users/password-reset/%{email_token} - - (如果链接已经过期,在登录时用,选择“我忘记了密码”,再次输入你的邮箱即可。) test_mailer: text_body_template: | 这是一封测试电子邮件,发自: @@ -1481,7 +1468,6 @@ zh_CN: disagreed: "谢谢你的消息。我们正在进行处理。" deferred: "感谢通知我们。我们正在调查情况。" deferred_and_deleted: "感谢通知我们。我们已经删除了帖子。" - temporarily_closed_due_to_flags: "主题因为大量的社群标记暂时关闭" system_messages: post_hidden: subject_template: "帖子因社群标记隐藏" @@ -1499,8 +1485,6 @@ zh_CN: 然而,如果帖子再次被社群成员标记并隐藏,它将被隐藏至版主处理后——并且可能导致进一步的措施,如封禁帐号。 想了解更多,请查看我们的[社群指引](%{base_url}/guidelines)。 - usage_tips: - text_body_template: "要查看给新用户的简要技巧,[(英文)看看这篇博客文章](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/)。\n\n只要你参与,我们将更了解你,并且新用户的临时限制将被移除。\b随着时间,你将获得[信任等级](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924),这将提供一些特殊功能来帮助我们更好地管理社群。\n" welcome_user: title: "欢迎用户" subject_template: "欢迎来到 %{site_name}!" @@ -1964,52 +1948,16 @@ zh_CN: back_to_top: "回到顶部" forgot_password: title: "忘记密码" - text_body_template: | - 有人请求重置你在 [%{site_name}](%{base_url}) 上的密码。 - - 如果那不是你,你可以直接忽略本邮件。 - - 点击下面的链接来选择一个新密码: - %{base_url}/users/password-reset/%{email_token} set_password: title: "设置密码" - text_body_template: | - 有人请求添加你在 [%{site_name}](%{base_url}) 的密码。除此之外,你可以通过已验证过你邮件地址的在线服务商登录。 - - 如果不是你在请求添加密码,你可以直接忽略本邮件。 - - 点击下面的链接来选择一个新密码: - %{base_url}/users/password-reset/%{email_token} admin_login: title: "管理员登陆" - text_body_template: | - 有人尝试登录你在[%{site_name}](%{base_url})的账户。 - - 如果不是你在操作,你可以安全地忽略这封邮件。 - - 点击下面的链接登录: - %{base_url}/users/admin-login/%{email_token} account_created: title: "帐号已创建" - text_body_template: | - 你在 %{site_name} 上建立了一个新账号 - - 点击下面的链接来为新账户设置密码: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "确认新邮箱" - text_body_template: | - 点击下面的链接来确认你在%{site_name}上的新电子邮箱地址: - - %{base_url}/users/authorize-email/%{email_token} confirm_old_email: title: "确认旧邮箱" - text_body_template: | - 在我们修改你的邮箱地址前,我们需要你确认你拥有当前的邮件账号。在你完成这步之后,我们将确认你的新邮件地址。 - - 点击下面的链接来确认你当前在%{site_name}上的电子邮箱地址: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: title: "通知旧邮箱" text_body_template: | @@ -2021,32 +1969,8 @@ zh_CN: signup_after_approval: title: "在审批之后注册" subject_template: "你已经被 %{site_name} 论坛批准加入了!" - text_body_template: | - 欢迎加入%{site_name}! - - 一个管理人员批准了你在%{site_name}的账户。 - - 点击下面的链接来确认并激活你在 %{site_name} 上的新账号: - %{base_url}/users/activate-account/%{email_token} - - 如果上面的链接无法点击,请拷贝该链接并粘贴到你的浏览器的地址栏里。 - - %{new_user_tips} - - 我们始终相信[讨论应该文明](%{base_url}/guidelines)。 - - 好好享受你在论坛的时光吧! - - (如果你在新用户级别需要和[管理人员](%{base_url/about)沟通的话,直接回复这个消息。) signup: title: "注册" - text_body_template: | - 欢迎来到 %{site_name}! - - 点击下面的链接来确认和激活你的新帐号: - %{base_url}/users/activate-account/%{email_token} - - 如果上面的链接无法点击,请拷贝该链接并粘贴到你的浏览器的地址栏里。 page_not_found: title: "抱歉!这个页面不存在或者是私密的。" popular_topics: "热门" @@ -2537,7 +2461,6 @@ zh_CN: 该徽章授予给第一次在帖子中使用 Emoji 的你 :thumbsup:。Emoji 让你在帖子中表达快乐 :smiley:、悲伤 :anguished:、愤怒 :angry: 以及任何东西 :sunglasses:。输入 :(冒号)或在编辑器中的 Emoji 工具栏中选择吧 :ok_hand: first_mention: name: 首次提及 - description: 在帖子中提到用户 long_description: 该徽章授予给第一次在帖子中@别人的你。每个提及都将通知对方,让他们知道你发了贴。输入 @ 提及任何用户,如果可以的话,也可以提及群组——这是个获得他们注意的好办法。 first_onebox: name: 首个Onebox @@ -2545,7 +2468,6 @@ zh_CN: long_description: 该徽章授予给第一次在空行中发表链接的你,链接将被展开成 onebox。其中有链接摘要、标题和(如果有)图片。 first_reply_by_email: name: 首次邮件回复 - description: 通过邮件回复 long_description: | 该徽章授予给第一次通过邮件回复的你 :e-mail:。 admin_login: @@ -2579,7 +2501,6 @@ zh_CN: safe_mode: title: "进入安全模式" description: "安全模式让你的站点不载入插件或站点自定义设置。" - no_customizations: "禁止所以站点自定义" only_official: "禁用非官方插件" no_plugins: "禁用所以插件" enter: "进入安全模式" diff --git a/config/locales/server.zh_TW.yml b/config/locales/server.zh_TW.yml index 5bbc42b189..5fb8cca8ca 100644 --- a/config/locales/server.zh_TW.yml +++ b/config/locales/server.zh_TW.yml @@ -27,7 +27,7 @@ zh_TW: topics: "討論話題" posts: "文章" loading: "載入中" - powered_by_html: 'Powered by Discourse, 請啟用 Javascript 以獲得最佳瀏覽效果' + powered_by_html: 'Powered by Discourse, 建議開啟 JavaScript 瀏覽' log_in: "登入" purge_reason: "自動當成遺棄、未啟動的帳戶刪除" disable_remote_images_download_reason: "磁盤空間不足,圖像下載已被禁用。" @@ -254,14 +254,6 @@ zh_TW: 這篇主題真的對你很重要 – 你在這個討論中已回覆多於 %{percent}% 的回應。 你有沒有提供其他人充足的時間,以便表達意見呢? - get_a_room: | - ### 考慮回應更多參與者 - - 你已經在這個特定主題中,回應了 @%{reply_username} %{count} 次。 - - 你有考慮回應討論中的 *其他* 人嗎?一個好的討論,需要納入更多人的觀點與意見。 - - 如果你還是想要與他繼續討論,請考慮透過[個人訊息](/users/%{reply_username})來進行。 too_many_replies: | ### 你發表的回應數量已經達到主題的回覆上限 @@ -311,16 +303,12 @@ zh_TW: attributes: hex: invalid: "不是一個有效顏色" - post_reply: - base: - different_topic: "貼文和回覆都必須處在相同的討論話題下。" web_hook: attributes: payload_url: invalid: "URL 不合法。URL 應包括 http:// 或 https://。並且其中沒有空格。" <<: *errors user_profile: - no_info_me: "
    你的自我簡介中,“關於我” 部分目前還沒有內容,不如來自我介紹一下?
    " no_info_other: "
    %{name} 尚未在他/她自我簡介中的“關於我”部分填寫任何資訊
    " vip_category_name: "貴賓室" vip_category_description: "信任等級高於 3 的用戶討論的分類" @@ -1338,7 +1326,6 @@ zh_TW: incorrect_username_email_or_password: "用戶名、電子郵箱或密碼不正確" wait_approval: "謝謝註冊帳號。我們會在你的帳號獲得批准之後通知你。" active: "你的帳號已經被啟用,可以使用了。" - activate_email: "

    快完成了!我們已寄送了一封確認信件到 %{email},請按照信中的步驟來啟用你的帳號。

    如果你沒有收到郵件,請檢查你的垃圾郵件收件夾,或者試試再登錄一次,以再次發送另一封確認信件。

    " not_activated: "你還不能登入。我們發送了一封啟用郵件給你,請按照郵件中的步驟來啟用你的帳號。" not_allowed_from_ip_address: "你無法透過此 IP 登入成為 %{username}。" admin_not_allowed_from_ip_address: "你無法透過此 IP 登入成為管理員。" @@ -1448,7 +1435,6 @@ zh_TW: %{invite_link} custom_invite_forum_mailer: - title: "邀請論壇 Mailer" subject_template: "%{invitee_name}邀請你加入%{site_domain_name}" text_body_template: | %{invitee_name} 留下以下留言 @@ -1467,13 +1453,6 @@ zh_TW: invite_password_instructions: title: "邀請密碼的說明" subject_template: "為 %{site_name} 的帳戶設置密碼" - text_body_template: | - 感謝你接受來自 %{site_name} 的邀請,歡迎! - - 點擊下面的連結,立即設定你的密碼: - %{base_url}/users/password-reset/%{email_token} - - (如果連結已經過期,在登入頁面選擇「我忘了我的密碼」,再次輸入你的 email 即可) test_mailer: title: "測試 Mailer" text_body_template: | @@ -1564,7 +1543,6 @@ zh_TW: disagreed: "感謝你讓我們知道。我們正在調查。" deferred: "感謝你讓我們知道。我們正在調查。" deferred_and_deleted: "感謝通知我們。我們已經刪除了帖子。" - temporarily_closed_due_to_flags: "主題因為大量的社群標記暫時關閉" system_messages: post_hidden: title: "文章已隱藏" @@ -1583,8 +1561,6 @@ zh_TW: 然而,如果帖子再次被社群成員標記並隱藏,它將被隱藏至版主處理後——並且可能導致進一步的措施,如封禁帳號。 想瞭解更多,請查看我們的[社群指引](%{base_url}/guidelines)。 - usage_tips: - text_body_template: "要查看給新用戶的簡要技巧,[(英文)看看這篇博客文章](http://blog.discourse.org/2016/12/discourse-new-user-tips-and-tricks/)。\n\n只要你參與,我們將更瞭解你,並且新用戶的臨時限制將被移除。\b隨着時間,你將獲得[信任等級](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924),這將提供一些特殊功能來幫助我們更好地管理社群。\n" welcome_user: title: "歡迎使用者" subject_template: "歡迎來到 %{site_name}!" @@ -2094,52 +2070,16 @@ zh_TW: back_to_top: "回到頂部" forgot_password: title: "忘記密碼" - text_body_template: | - 有人請求重設你在 [%{site_name}](%{base_url}) 上的密碼。 - - 如果那不是你,你可以直接忽略本郵件。 - - 點擊下面的連結來選擇一個新密碼: - %{base_url}/users/password-reset/%{email_token} set_password: title: "設定密碼" - text_body_template: | - 有人請求添加你在 [%{site_name}](%{base_url}) 的密碼。除此之外,你可以通過已驗證過你郵件地址的在綫服務商登錄。 - - 如果不是你在請求添加密碼,你可以直接忽略本郵件。 - - 點擊下面的連結來選擇一個新密碼: - %{base_url}/users/password-reset/%{email_token} admin_login: title: "管理員登入" - text_body_template: | - 有人嘗試登錄你在[%{site_name}](%{base_url})的賬戶。 - - 如果不是你在操作,你可以安全地忽略這封郵件。 - - 點擊下面的連結登錄: - %{base_url}/users/admin-login/%{email_token} account_created: title: "建立用戶" - text_body_template: | - 你在 %{site_name} 上建立了一個新賬號 - - 點擊下面的連結來為新賬戶設置密碼: - %{base_url}/users/password-reset/%{email_token} confirm_new_email: title: "確認新郵件" - text_body_template: | - 點擊以下連結確認你在[%{site_name}]上的新電子郵件: - - %{base_url}/users/authorize-email/%{email_token} confirm_old_email: title: "確認舊郵件" - text_body_template: | - 在我們修改你的郵箱地址前,我們需要你確認你擁有當前的郵件賬號。在你完成這步之後,我們將確認你的新郵件地址。 - - 點擊下面的連結來確認你當前在%{site_name}上的電子郵箱地址: - - %{base_url}/users/authorize-email/%{email_token} notify_old_email: title: "通知舊的郵件" text_body_template: | @@ -2151,32 +2091,8 @@ zh_TW: signup_after_approval: title: "在同意之後註冊" subject_template: "你已受到 %{site_name} 的認可!" - text_body_template: | - 歡迎加入 %{site_name}! - - 管理人員已批准了你在 %{site_name} 的帳戶申請。 - - 請點擊以下連結,以確認和啟用你的新帳號: - %{base_url}/users/activate-account/%{email_token} - - 如果上面的連結無法點擊,請複製該連結,並貼到瀏覽器中開啟。 - - %{new_user_tips} - - 我們始終相信[社群守則](%{base_url}/guidelines)。 - - 好好享受你在論壇的時光吧! - - (如果你需要和[管理人員](%{base_url}/about)聯繫的話,請直接回覆這則訊息。) signup: title: "註冊" - text_body_template: | - 歡迎來到 %{site_name}! - - 請點擊以下連結,以確認和啟用你的新帳號: - %{base_url}/users/activate-account/%{email_token} - - 如果上面的連結無法點擊,請複製該連結,並貼到瀏覽器中開啟。 page_not_found: title: "抱歉!這個頁面不存在或者是私密的。" popular_topics: "熱門" @@ -2667,7 +2583,6 @@ zh_TW: 該徽章授予給第一次在帖子中使用 Emoji 的你 :thumbsup:。Emoji 讓你在帖子中表達快樂 :smiley:、悲傷 :anguished:、憤怒 :angry: 以及任何東西 :sunglasses:。輸入 :(冒號)或在編輯器中的 Emoji 工具欄中選擇吧 :ok_hand: first_mention: name: 首次提及 - description: 在帖子中提到用戶 long_description: 該徽章授予給第一次在帖子中@別人的你。每個提及都將通知對方,讓他們知道你發了貼。輸入 @ 提及任何用戶,如果可以的話,也可以提及群組——這是個獲得他們注意的好辦法。 first_onebox: name: 首個Onebox @@ -2675,7 +2590,6 @@ zh_TW: long_description: 該徽章授予給第一次在空行中發錶鏈接的你,連結將被展開成 onebox。其中有連結摘要、標題和(如果有)圖片。 first_reply_by_email: name: 首次郵件回覆 - description: 通過郵件回覆 long_description: | 該徽章授予給第一次通過郵件回覆的你 :e-mail:。 admin_login: @@ -2709,7 +2623,6 @@ zh_TW: safe_mode: title: "進入安全模式" description: "安全模式讓你的站點不載入插件或站點自定義設置。" - no_customizations: "禁止所以站點自定義" only_official: "禁用非官方插件" no_plugins: "禁用所以插件" enter: "進入安全模式" diff --git a/config/routes.rb b/config/routes.rb index e6c3d9ed71..ba09b3321d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,8 +56,6 @@ Discourse::Application.routes.draw do get "site/basic-info" => 'site#basic_info' get "site/statistics" => 'site#statistics' - get "site_customizations/:key" => "site_customizations#show" - get "srv/status" => "forums#status" get "wizard" => "wizard#index" @@ -97,6 +95,7 @@ Discourse::Application.routes.draw do resources :users, id: USERNAME_ROUTE_FORMAT, except: [:show] do collection do + get "list" => "users#index" get "list/:query" => "users#index" get "ip-info" => "users#ip_info" delete "delete-others-with-same-ip" => "users#delete_other_accounts_with_same_ip" @@ -162,6 +161,7 @@ Discourse::Application.routes.draw do scope "/logs" do resources :staff_action_logs, only: [:index] + get 'staff_action_logs/:id/diff' => 'staff_action_logs#diff' resources :screened_emails, only: [:index, :destroy] resources :screened_ip_addresses, only: [:index, :create, :update, :destroy] do collection do @@ -174,9 +174,9 @@ Discourse::Application.routes.draw do get "/logs" => "staff_action_logs#index" get "customize" => "color_schemes#index", constraints: AdminConstraint.new - get "customize/css_html" => "site_customizations#index", constraints: AdminConstraint.new - get "customize/css_html/:id/:section" => "site_customizations#index", constraints: AdminConstraint.new + get "customize/themes" => "themes#index", constraints: AdminConstraint.new get "customize/colors" => "color_schemes#index", constraints: AdminConstraint.new + get "customize/colors/:id" => "color_schemes#index", constraints: AdminConstraint.new get "customize/permalinks" => "permalinks#index", constraints: AdminConstraint.new get "customize/embedding" => "embedding#show", constraints: AdminConstraint.new put "customize/embedding" => "embedding#update", constraints: AdminConstraint.new @@ -186,12 +186,18 @@ Discourse::Application.routes.draw do post "flags/agree/:id" => "flags#agree" post "flags/disagree/:id" => "flags#disagree" post "flags/defer/:id" => "flags#defer" - resources :site_customizations, constraints: AdminConstraint.new + + resources :themes, constraints: AdminConstraint.new + post "themes/import" => "themes#import" + get "themes/:id/preview" => "themes#preview" scope "/customize", constraints: AdminConstraint.new do resources :user_fields, constraints: AdminConstraint.new resources :emojis, constraints: AdminConstraint.new + get 'themes/:id/:target/:field_name/edit' => 'themes#index' + get 'themes/:id' => 'themes#index' + # They have periods in their URLs often: get 'site_texts' => 'site_texts#index' get 'site_texts/(:id)' => 'site_texts#show', constraints: { id: /[\w.\-]+/i } @@ -385,7 +391,8 @@ Discourse::Application.routes.draw do get "highlight-js/:hostname/:version.js" => "highlight_js#show", format: false, constraints: { hostname: /[\w\.-]+/ } - get "stylesheets/:name.css" => "stylesheets#show", constraints: { name: /[a-z0-9_]+/ } + get "stylesheets/:name.css.map" => "stylesheets#show_source_map", constraints: { name: /[-a-z0-9_]+/ } + get "stylesheets/:name.css" => "stylesheets#show", constraints: { name: /[-a-z0-9_]+/ } post "uploads" => "uploads#create" @@ -708,6 +715,8 @@ Discourse::Application.routes.draw do get "/safe-mode" => "safe_mode#index" post "/safe-mode" => "safe_mode#enter", as: "safe_mode_enter" + get "/themes/assets/:key" => "themes#assets" + get "*url", to: 'permalinks#show', constraints: PermalinkConstraint.new end diff --git a/config/site_settings.yml b/config/site_settings.yml index 09bead2a13..826f0a7a14 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -82,10 +82,11 @@ basic: categories_topics: default: 20 min: 5 + max: 2000 suggested_topics: client: true default: 5 - min: 0 + max: 2000 limit_suggested_to_category: default: false suggested_topics_max_days_old: @@ -184,10 +185,11 @@ basic: enable_mobile_theme: client: true default: true + default_theme_key: + hidden: true relative_date_duration: client: true default: 30 - min: 0 topics_per_period_in_top_summary: default: 20 min: 1 @@ -330,6 +332,7 @@ users: client: true default: 3 min: 1 + max: 60 max_username_length: client: true default: 20 @@ -342,13 +345,16 @@ users: client: true default: 10 min: 8 + max: 2000 min_admin_password_length: client: true default: 15 min: 8 + max: 2000 password_unique_characters: default: 6 min: 1 + max: 2000 block_common_passwords: true enforce_global_nicknames: default: false @@ -369,18 +375,18 @@ users: default: true invite_expiry_days: default: 30 - min: 0 - max: 20000 invite_passthrough_hours: 0 invites_per_page: client: true default: 40 + min: 1 delete_user_max_post_age: client: true default: 60 delete_all_posts_max: client: true default: 15 + min: 1 redirect_users_to_top_page: true show_email_on_profile: client: true @@ -393,8 +399,6 @@ users: min: 1 purge_unactivated_users_grace_period_days: default: 14 - min: 0 - max: 20000 public_user_custom_fields: type: list default: '' @@ -413,6 +417,7 @@ users: client: true anonymous_account_duration_minutes: default: 10080 + max: 99000 hide_user_profiles_from_public: default: false client: true @@ -447,6 +452,7 @@ posting: max_post_length: client: true default: 32000 + max: 99000 topic_featured_link_enabled: client: true default: true @@ -488,7 +494,6 @@ posting: delete_removed_posts_after: client: true default: 24 - min: 0 traditional_markdown_linebreaks: client: true default: false @@ -531,12 +536,11 @@ posting: default: false show_time_gap_days: default: 7 - min: 0 - max: 20000 client: true short_progress_text_threshold: client: true default: 10000 + max: 99000 default_code_lang: client: true default: "auto" @@ -579,7 +583,6 @@ posting: enum: 'TrustLevelSetting' notify_about_queued_posts_after: default: 24 - min: 0 auto_close_messages_post_count: 500 auto_close_topics_post_count: 10000 code_formatting_style: @@ -610,8 +613,6 @@ email: digest_other_topics: 5 suppress_digest_email_after_days: default: 365 - min: 0 - max: 20000 digest_suppress_categories: type: category_list default: '' @@ -645,8 +646,7 @@ email: pop3_polling_username: '' pop3_polling_password: '' log_mail_processing_failures: false - incoming_email_prefer_html: - default: false + incoming_email_prefer_html: false email_in: default: false client: true @@ -668,8 +668,6 @@ email: default: false delete_email_logs_after_days: default: 365 - min: 0 - max: 20000 max_emails_per_day_per_user: 100 enable_staged_users: true maximum_staged_users_per_email: 10 @@ -696,8 +694,6 @@ email: min: 2 reset_bounce_score_after_days: default: 30 - min: 0 - max: 20000 attachment_content_type_blacklist: type: list default: "pkcs7|x-vcard" @@ -706,18 +702,21 @@ email: default: "smime.p7s|signature.asc" enable_forwarded_emails: false always_show_trimmed_content: false + private_email: false files: max_image_size_kb: client: true default: 3072 + max: 102400 max_attachment_size_kb: client: true default: 3072 + max: 1024000 max_image_megapixels: default: 40 min: 5 - max: 100 + max: 150 authorized_extensions: client: true default: 'jpg|jpeg|png|gif' @@ -750,8 +749,6 @@ files: clean_orphan_uploads_grace_period_hours: 48 purge_deleted_uploads_grace_period_days: default: 30 - min: 0 - max: 20000 prevent_anons_from_downloading_files: default: false client: true @@ -836,8 +833,6 @@ trust: tl2_requires_time_spent_mins: 60 tl2_requires_days_visited: default: 15 - min: 0 - max: 20000 tl2_requires_likes_received: 1 tl2_requires_likes_given: 1 tl2_requires_topic_reply_count: 3 @@ -847,44 +842,31 @@ trust: max: 1000000 tl3_requires_days_visited: default: 50 - min: 0 - max: 20000 tl3_requires_topics_replied_to: default: 10 - min: 0 tl3_requires_topics_viewed: default: 25 - min: 0 max: 100 tl3_requires_topics_viewed_cap: default: 500 - min: 0 tl3_requires_posts_read: default: 25 - min: 0 max: 100 tl3_requires_posts_read_cap: default: 20000 - min: 0 + max: 99000 tl3_requires_topics_viewed_all_time: default: 200 - min: 0 tl3_requires_posts_read_all_time: default: 500 - min: 0 tl3_requires_max_flagged: default: 5 - min: 0 tl3_promotion_min_duration: default: 14 - min: 0 - max: 20000 tl3_requires_likes_given: default: 30 - min: 0 tl3_requires_likes_received: default: 20 - min: 0 tl3_links_no_follow: default: false client: true @@ -937,7 +919,6 @@ spam: type: list levenshtein_distance_spammer_emails: default: 2 - min: 0 max: 3 max_new_accounts_per_registration_ip: 3 min_ban_entries_for_roll_up: 5 @@ -989,11 +970,9 @@ rate_limits: client: true max_logins_per_ip_per_hour: min: 1 - max: 20000 default: 30 max_logins_per_ip_per_minute: min: 1 - max: 20000 default: 6 developer: @@ -1026,12 +1005,15 @@ developer: background_polling_interval: client: true default: 60000 + max: 99000 polling_interval: client: true default: 3000 + max: 99000 anon_polling_interval: client: true default: 15000 + max: 99000 flush_timings_secs: client: true default: 20 @@ -1041,13 +1023,10 @@ developer: client: true top_topics_formula_log_views_multiplier: default: 2 - min: 0 top_topics_formula_first_post_likes_multiplier: default: 0.5 - min: 0 top_topics_formula_least_likes_per_post_multiplier: default: 3 - min: 0 rebake_old_posts_count: default: 250 min: 1 @@ -1213,18 +1192,12 @@ uncategorized: # Cold map thresholds cold_age_days_low: default: 14 - min: 0 - max: 20000 client: true cold_age_days_medium: default: 90 - min: 0 - max: 20000 client: true cold_age_days_high: default: 180 - min: 0 - max: 20000 client: true # Warnings @@ -1275,8 +1248,6 @@ uncategorized: delete_drafts_older_than_n_days: default: 180 - min: 0 - max: 20000 tos_topic_id: default: -1 @@ -1291,7 +1262,6 @@ uncategorized: bootstrap_mode_min_users: default: 50 client: true - min: 0 max: 5000 bootstrap_mode_enabled: diff --git a/config/unicorn.conf.rb b/config/unicorn.conf.rb index 5f51df510a..2e142c1b0d 100644 --- a/config/unicorn.conf.rb +++ b/config/unicorn.conf.rb @@ -36,6 +36,28 @@ preload_app true # fast LAN. check_client_connection false +@stats_socket_dir = ENV["UNICORN_STATS_SOCKET_DIR"] + +def clean_up_stats_socket(server, pid) + if @stats_socket_dir.present? + name = "#{@stats_socket_dir}/#{pid}.sock" + FileUtils.rm_f(name) + server.logger.info "Cleaned up stats socket at #{name}" + end +rescue => e + server.logger.warn "Failed to clean up stats socket #{e}" +end + +def start_stats_socket(server) + if @stats_socket_dir.present? + name = "#{@stats_socket_dir}/#{Process.pid}.sock" + StatsSocket.new(name).start + server.logger.info "Started stats socket at #{name}" + end +rescue => e + server.logger.warn "Failed to start stats socket #{e}" +end + initialized = false before_fork do |server, worker| @@ -51,6 +73,18 @@ before_fork do |server, worker| # router warm up Rails.application.routes.recognize_path('abc') rescue nil + if @stats_socket_dir.present? + server.logger.info "Initializing stats socket at #{@stats_socket_dir}" + begin + require 'stats_socket' + FileUtils.mkdir_p @stats_socket_dir + FileUtils.rm_f Dir.glob("#{@stats_socket_dir}/*.sock") + start_stats_socket(server) + rescue => e + server.logger.info "Failed to initialize stats socket dir #{e}" + end + end + # get rid of rubbish so we don't share it GC.start @@ -75,6 +109,11 @@ before_fork do |server, worker| require 'demon/sidekiq' + if @stats_socket_dir + Demon::Sidekiq.after_fork do + start_stats_socket(server) + end + end Demon::Sidekiq.start(sidekiqs) Signal.trap("SIGTSTP") do @@ -168,12 +207,17 @@ before_fork do |server, worker| sleep 1 end +after_worker_exit do |server, worker, status| + clean_up_stats_socket(server, status.pid) +end + after_fork do |server, worker| + start_stats_socket(server) + # warm up v8 after fork, that way we do not fork a v8 context # it may cause issues if bg threads in a v8 isolate randomly stop # working due to fork Discourse.after_fork - begin PrettyText.cook("warm up **pretty text**") rescue => e diff --git a/db/migrate/20170313192741_add_themes.rb b/db/migrate/20170313192741_add_themes.rb new file mode 100644 index 0000000000..ab2e3abc3b --- /dev/null +++ b/db/migrate/20170313192741_add_themes.rb @@ -0,0 +1,79 @@ +class AddThemes < ActiveRecord::Migration + def up + rename_table :site_customizations, :themes + + add_column :themes, :user_selectable, :bool, null: false, default: false + add_column :themes, :hidden, :bool, null: false, default: false + add_column :themes, :color_scheme_id, :integer + + create_table :child_themes do |t| + t.integer :parent_theme_id + t.integer :child_theme_id + t.timestamps + end + + add_index :child_themes, [:parent_theme_id, :child_theme_id], unique: true + add_index :child_themes, [:child_theme_id, :parent_theme_id], unique: true + + # versioning in color scheme table was very confusing, remove it + execute "DELETE FROM color_schemes WHERE versioned_id IS NOT NULL" + remove_column :color_schemes, :versioned_id + + enabled_theme_count = execute("SELECT count(*) FROM themes WHERE enabled") + .to_a[0]["count"].to_i + + + enabled_scheme_id = execute("SELECT id FROM color_schemes WHERE enabled") + .to_a[0]&.fetch("id") + + theme_key, theme_id = + execute("SELECT key, id FROM themes WHERE enabled").to_a[0]&.values + + if (enabled_theme_count == 0 && enabled_scheme_id) || enabled_theme_count > 1 + + puts "Creating a new default theme!" + + theme_key = '7e202ef2-6666-47d5-98d8-a9c8d15e57dd' + + sql = < 1 + execute < 0) + puts "Setting default theme" + sql = < 0 +SQL + remove_column :themes, value + end + + %w{ head_tag_baked + body_tag_baked + header_baked + footer_baked + mobile_footer_baked + mobile_header_baked + }.each do |col| + remove_column :themes, col + end + end +end diff --git a/db/migrate/20170328203122_add_compiler_version_to_theme_fields.rb b/db/migrate/20170328203122_add_compiler_version_to_theme_fields.rb new file mode 100644 index 0000000000..7f66ff7eae --- /dev/null +++ b/db/migrate/20170328203122_add_compiler_version_to_theme_fields.rb @@ -0,0 +1,5 @@ +class AddCompilerVersionToThemeFields < ActiveRecord::Migration + def change + add_column :theme_fields, :compiler_version, :integer, null: false, default: 0 + end +end diff --git a/db/migrate/20170407154510_rename_theme_id.rb b/db/migrate/20170407154510_rename_theme_id.rb new file mode 100644 index 0000000000..48d3524f64 --- /dev/null +++ b/db/migrate/20170407154510_rename_theme_id.rb @@ -0,0 +1,5 @@ +class RenameThemeId < ActiveRecord::Migration + def change + rename_column :color_schemes, :theme_id, :base_scheme_id + end +end diff --git a/db/migrate/20170410170923_add_theme_remote_fields.rb b/db/migrate/20170410170923_add_theme_remote_fields.rb new file mode 100644 index 0000000000..95cb5d7e5c --- /dev/null +++ b/db/migrate/20170410170923_add_theme_remote_fields.rb @@ -0,0 +1,17 @@ +class AddThemeRemoteFields < ActiveRecord::Migration + def change + create_table :remote_themes do |t| + t.string :remote_url, null: false + t.string :remote_version + t.string :local_version + t.string :about_url + t.string :license_url + t.integer :commits_behind + t.datetime :remote_updated_at + t.timestamps + end + + add_column :themes, :remote_theme_id, :integer + add_index :themes, :remote_theme_id, unique: true + end +end diff --git a/db/migrate/20170413043152_rename_warnings.rb b/db/migrate/20170413043152_rename_warnings.rb new file mode 100644 index 0000000000..d4397726be --- /dev/null +++ b/db/migrate/20170413043152_rename_warnings.rb @@ -0,0 +1,9 @@ +class RenameWarnings < ActiveRecord::Migration + def up + rename_table :warnings, :user_warnings + end + + def down + rename_table :user_warnings, :warnings + end +end diff --git a/db/migrate/20170417164715_add_theme_id_to_color_scheme.rb b/db/migrate/20170417164715_add_theme_id_to_color_scheme.rb new file mode 100644 index 0000000000..884621c776 --- /dev/null +++ b/db/migrate/20170417164715_add_theme_id_to_color_scheme.rb @@ -0,0 +1,5 @@ +class AddThemeIdToColorScheme < ActiveRecord::Migration + def change + add_column :color_schemes, :theme_id, :int + end +end diff --git a/db/migrate/20170419193714_add_error_to_theme_fields.rb b/db/migrate/20170419193714_add_error_to_theme_fields.rb new file mode 100644 index 0000000000..34bcaa4754 --- /dev/null +++ b/db/migrate/20170419193714_add_error_to_theme_fields.rb @@ -0,0 +1,5 @@ +class AddErrorToThemeFields < ActiveRecord::Migration + def change + add_column :theme_fields, :error, :string + end +end diff --git a/db/migrate/20170420163628_add_default_notification_level_to_group.rb b/db/migrate/20170420163628_add_default_notification_level_to_group.rb new file mode 100644 index 0000000000..dee6ae771e --- /dev/null +++ b/db/migrate/20170420163628_add_default_notification_level_to_group.rb @@ -0,0 +1,11 @@ +class AddDefaultNotificationLevelToGroup < ActiveRecord::Migration + def up + add_column :groups, :default_notification_level, :integer, default: 3, null: false + # don't auto watch 'moderators' it is just way too loud + execute 'UPDATE groups SET default_notification_level = 2 WHERE id = 2' + end + + def down + remove_column :groups, :default_notification_level + end +end diff --git a/db/migrate/20170425083011_add_deleted_at_to_topic_embeds.rb b/db/migrate/20170425083011_add_deleted_at_to_topic_embeds.rb new file mode 100644 index 0000000000..5aaf8ed836 --- /dev/null +++ b/db/migrate/20170425083011_add_deleted_at_to_topic_embeds.rb @@ -0,0 +1,6 @@ +class AddDeletedAtToTopicEmbeds < ActiveRecord::Migration + def change + add_column :topic_embeds, :deleted_at, :datetime + add_column :topic_embeds, :deleted_by_id, :integer, null: true + end +end diff --git a/db/migrate/20170425172415_add_error_to_scheduler_stats.rb b/db/migrate/20170425172415_add_error_to_scheduler_stats.rb new file mode 100644 index 0000000000..9d26ec29d7 --- /dev/null +++ b/db/migrate/20170425172415_add_error_to_scheduler_stats.rb @@ -0,0 +1,5 @@ +class AddErrorToSchedulerStats < ActiveRecord::Migration + def change + add_column :scheduler_stats, :error, :text + end +end diff --git a/docs/INSTALL.md b/docs/INSTALL.md index f523b49db9..b58adc60d0 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -14,9 +14,10 @@ Hosting Rails applications is complicated. Even if you already have Postgres, Re ### Hardware Requirements -- Dual core CPU recommended +- modern single core CPU, dual core recommended - 1 GB RAM minimum (with [swap][swap]) - 64 bit Linux compatible with Docker +- 10 GB disk space minimum ### Software Requirements diff --git a/lib/autospec/manager.rb b/lib/autospec/manager.rb index 4505dd905f..4f9089c9dc 100644 --- a/lib/autospec/manager.rb +++ b/lib/autospec/manager.rb @@ -3,6 +3,7 @@ require "thread" require "fileutils" require "autospec/reload_css" require "autospec/base_runner" +require "socket_server" module Autospec; end @@ -173,18 +174,39 @@ class Autospec::Manager path = File.expand_path(File.dirname(__FILE__) + "../../..") + if ENV['VIM_AUTOSPEC'] + STDERR.puts "Using VIM file listener" + + socket_path = (Rails.root + "tmp/file_change.sock").to_s + FileUtils.rm_f(socket_path) + server = SocketServer.new(socket_path) + server.start do |line| + file,line = line.split(' ') + file = file.sub(Rails.root.to_s << "/", "") + # process_change can aquire a mutex and block + # the acceptor + Thread.new do + process_change([[file,line]]) + end + "OK" + end + return + end + # to speed up boot we use a thread ["spec", "lib", "app", "config", "test", "vendor", "plugins"].each do |watch| puts "@@@@@@@@@ Listen to #{path}/#{watch} #{options}" if @debug Thread.new do begin - Listen.to("#{path}/#{watch}", options) do |modified, added, _| + listener = Listen.to("#{path}/#{watch}", options) do |modified, added, _| paths = [modified, added].flatten paths.compact! paths.map!{|long| long[(path.length+1)..-1]} process_change(paths) end + listener.start + sleep rescue => e puts "FAILED to listen on changes to #{path}/#{watch}" puts e @@ -202,7 +224,7 @@ class Autospec::Manager specs = [] hit = false - files.each do |file| + files.each do |file, line| @runners.each do |runner| # reloaders runner.reloaders.each do |k| @@ -218,20 +240,21 @@ class Autospec::Manager puts "@@@@@@@@@@@@ #{file} matched a watcher for #{runner}" if @debug hit = true spec = v ? (v.arity == 1 ? v.call(m) : v.call) : file - specs << [file, spec, runner] if File.exists?(spec) || Dir.exists?(spec) + with_line = spec + if spec == file && line + with_line = spec + ":" << line.to_s + end + if File.exists?(spec) || Dir.exists?(spec) + if with_line != spec + specs << [file, spec, runner] + end + specs << [file, with_line, runner] + end end end end - # special watcher for styles/templates - Autospec::ReloadCss::WATCHERS.each do |k, _| - matches = [] - matches << file if k.match(file) - Autospec::ReloadCss.run_on_change(matches) if matches.present? - end end - queue_specs(specs) if hit - rescue => e fail(e, "failed in watcher") end @@ -256,7 +279,7 @@ class Autospec::Manager puts "@@@@@@@@@@@@ #{@queue}" if @debug specs.each do |file, spec, runner| # make sure there's no other instance of this spec in the queue - @queue.delete_if { |_, s, r| s.strip == spec.strip && r == runner } + @queue.delete_if { |_, s, r| s.strip.start_with?(spec.strip) && r == runner } # deal with focused specs if @queue.first && @queue.first[0] == "focus" focus = @queue.shift diff --git a/lib/autospec/reload_css.rb b/lib/autospec/reload_css.rb index e6bd00a6fa..379058666c 100644 --- a/lib/autospec/reload_css.rb +++ b/lib/autospec/reload_css.rb @@ -30,7 +30,6 @@ class Autospec::ReloadCss [:desktop, :mobile].each do |style| s = DiscourseStylesheets.new(style) s.compile - s.ensure_digestless_file paths << "public" + s.stylesheet_relpath_no_digest end ActiveRecord::Base.clear_active_connections! diff --git a/lib/autospec/simple_runner.rb b/lib/autospec/simple_runner.rb index e94d3d9341..be8da854c7 100644 --- a/lib/autospec/simple_runner.rb +++ b/lib/autospec/simple_runner.rb @@ -7,14 +7,16 @@ module Autospec def run(specs) puts "Running Rspec: " << specs # kill previous rspec instance - abort + self.abort # we use our custom rspec formatter args = ["-r", "#{File.dirname(__FILE__)}/formatter.rb", "-f", "Autospec::Formatter", specs.split].flatten.join(" ") # launch rspec - @pid = Process.spawn({"RAILS_ENV" => "test"}, "bundle exec rspec #{args}") - _, status = Process.wait2(@pid) - status.exitstatus + Dir.chdir(Rails.root) do + @pid = Process.spawn({"RAILS_ENV" => "test"}, "bin/rspec #{args}") + _, status = Process.wait2(@pid) + status.exitstatus + end end def abort diff --git a/lib/demon/sidekiq.rb b/lib/demon/sidekiq.rb index 4e984bc781..350b2da28b 100644 --- a/lib/demon/sidekiq.rb +++ b/lib/demon/sidekiq.rb @@ -6,6 +6,10 @@ class Demon::Sidekiq < Demon::Base "sidekiq" end + def self.after_fork(&blk) + blk ? (@blk=blk) : @blk + end + private def suppress_stdout @@ -17,6 +21,8 @@ class Demon::Sidekiq < Demon::Base end def after_fork + Demon::Sidekiq.after_fork&.call + STDERR.puts "Loading Sidekiq in process id #{Process.pid}" require 'sidekiq/cli' # CLI will close the logger, if we have one set we can be in big @@ -25,8 +31,19 @@ class Demon::Sidekiq < Demon::Base # parent process is in charge of the file anyway. Sidekiq::Logging.logger = nil cli = Sidekiq::CLI.instance - cli.parse(["-c", GlobalSetting.sidekiq_workers.to_s, "-q", "critical,4", "-q", "default,2", "-q", "low"]) + options = ["-c", GlobalSetting.sidekiq_workers.to_s] + + [['critical', 4], ['default', 2], ['low', 1]].each do |queue_name, weight| + custom_queue_hostname = ENV["UNICORN_SIDEKIQ_#{queue_name.upcase}_QUEUE_HOSTNAME"] + + if !custom_queue_hostname || custom_queue_hostname == `hostname`.strip + options << "-q" + options << "#{queue_name},#{weight}" + end + end + + cli.parse(options) load Rails.root + "config/initializers/100-sidekiq.rb" cli.run rescue => e diff --git a/lib/discourse_plugin_registry.rb b/lib/discourse_plugin_registry.rb index 1eee3fca07..568c48932d 100644 --- a/lib/discourse_plugin_registry.rb +++ b/lib/discourse_plugin_registry.rb @@ -13,7 +13,6 @@ class DiscoursePluginRegistry attr_writer :handlebars attr_writer :serialized_current_user_fields attr_writer :seed_data - attr_accessor :custom_html def plugins @@ -60,6 +59,15 @@ class DiscoursePluginRegistry def seed_data @seed_data ||= HashWithIndifferentAccess.new({}) end + + def html_builders + @html_builders ||= {} + end + + def vendored_pretty_text + @vendored_pretty_text ||= Set.new + end + end def register_js(filename, options={}) @@ -104,6 +112,8 @@ class DiscoursePluginRegistry if asset =~ JS_REGEX if opts == :admin self.admin_javascripts << asset + elsif opts == :vendored_pretty_text + self.vendored_pretty_text << asset else self.javascripts << asset end @@ -117,7 +127,6 @@ class DiscoursePluginRegistry else self.stylesheets << asset end - elsif asset =~ HANDLEBARS_REGEX self.handlebars << asset end @@ -127,6 +136,14 @@ class DiscoursePluginRegistry self.seed_data[key] = value end + def self.register_html_builder(name, &block) + html_builders[name] = block + end + + def self.build_html(name, ctx=nil) + html_builders[name]&.call(ctx) + end + def javascripts self.class.javascripts end @@ -169,6 +186,8 @@ class DiscoursePluginRegistry sass_variables.clear serialized_current_user_fields asset_globs.clear + html_builders.clear + vendored_pretty_text.clear end def self.setup(plugin_class) diff --git a/lib/email/html_cleaner.rb b/lib/email/html_cleaner.rb deleted file mode 100644 index e8c89f7a1b..0000000000 --- a/lib/email/html_cleaner.rb +++ /dev/null @@ -1,132 +0,0 @@ -module Email - # HtmlCleaner cleans up the extremely dirty HTML that many email clients - # generate by stripping out any excess divs or spans, removing styling in - # the process (which also makes the html more suitable to be parsed as - # Markdown). - class HtmlCleaner - # Elements to hoist all children out of - HTML_HOIST_ELEMENTS = %w(div span font table tbody th tr td) - # Node types to always delete - HTML_DELETE_ELEMENT_TYPES = [ - Nokogiri::XML::Node::DTD_NODE, - Nokogiri::XML::Node::COMMENT_NODE, - ] - - # Private variables: - # @doc - nokogiri document - # @out - same as @doc, but only if trimming has occured - def initialize(html) - if String === html - @doc = Nokogiri::HTML(html) - else - @doc = html - end - end - - class << self - # Email::HtmlCleaner.trim(inp, opts={}) - # - # Arguments: - # inp - Either a HTML string or a Nokogiri document. - # Options: - # :return => :doc, :string - # Specify the desired return type. - # Defaults to the type of the input. - # A value of :string is equivalent to calling get_document_text() - # on the returned document. - def trim(inp, opts={}) - cleaner = HtmlCleaner.new(inp) - - opts[:return] ||= ((String === inp) ? :string : :doc) - - if opts[:return] == :string - cleaner.output_html - else - cleaner.output_document - end - end - - # Email::HtmlCleaner.get_document_text(doc) - # - # Get the body portion of the document, including html, as a string. - def get_document_text(doc) - body = doc.xpath('//body') - if body - body.inner_html - else - doc.inner_html - end - end - end - - def output_document - @out ||= begin - doc = @doc - trim_process_node doc - add_newlines doc - doc - end - end - - def output_html - HtmlCleaner.get_document_text(output_document) - end - - private - - def add_newlines(doc) - # Replace
    tags with a markdown \n - doc.xpath('//br').each do |br| - br.replace(new_linebreak_node doc, 2) - end - # Surround

    tags with newlines, to help with line-wise postprocessing - # and ensure markdown paragraphs - doc.xpath('//p').each do |p| - p.before(new_linebreak_node doc) - p.after(new_linebreak_node doc, 2) - end - end - - def new_linebreak_node(doc, count=1) - Nokogiri::XML::Text.new("\n" * count, doc) - end - - def trim_process_node(node) - if should_hoist?(node) - hoisted = trim_hoist_element node - hoisted.each { |child| trim_process_node child } - elsif should_delete?(node) - node.remove - else - if children = node.children - children.each { |child| trim_process_node child } - end - end - - node - end - - def trim_hoist_element(element) - hoisted = [] - element.children.each do |child| - element.before(child) - hoisted << child - end - element.remove - hoisted - end - - def should_hoist?(node) - return false unless node.element? - HTML_HOIST_ELEMENTS.include? node.name - end - - def should_delete?(node) - return true if HTML_DELETE_ELEMENT_TYPES.include? node.type - return true if node.element? && node.name == 'head' - return true if node.text? && node.text.strip.blank? - - false - end - end -end diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 2322970bae..562c232443 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -1,7 +1,7 @@ require "digest" require_dependency "new_post_manager" require_dependency "post_action_creator" -require_dependency "email/html_cleaner" +require_dependency "html_to_markdown" module Email @@ -188,17 +188,21 @@ module Email text = fix_charset(@mail) end - if html.present? && (SiteSetting.incoming_email_prefer_html || text.blank?) - html = Email::HtmlCleaner.new(html).output_html - html = trim_discourse_markers(html) - html, elided = EmailReplyTrimmer.trim(html, true) - return [html, elided] + text, elided_text = if text.present? + text = trim_discourse_markers(text) + EmailReplyTrimmer.trim(text, true) end - if text.present? - text = trim_discourse_markers(text) - text, elided = EmailReplyTrimmer.trim(text, true) - return [text, elided] + markdown, elided_markdown = if html.present? + markdown = HtmlToMarkdown.new(html).to_markdown + markdown = trim_discourse_markers(markdown) + EmailReplyTrimmer.trim(markdown, true) + end + + if text.blank? || (SiteSetting.incoming_email_prefer_html && markdown.present?) + return [markdown, elided_markdown] + else + return [text, elided_text] end end @@ -553,7 +557,7 @@ module Email def create_post_with_attachments(options={}) # deal with attachments attachments.each do |attachment| - tmp = Tempfile.new("discourse-email-attachment") + tmp = Tempfile.new(["discourse-email-attachment", File.extname(attachment.filename)]) begin # read attachment File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded } diff --git a/lib/email/sender.rb b/lib/email/sender.rb index cf9695583c..db167a12dd 100644 --- a/lib/email/sender.rb +++ b/lib/email/sender.rb @@ -130,7 +130,14 @@ module Email # https://www.ietf.org/rfc/rfc3834.txt @message.header['Precedence'] = 'list' @message.header['List-ID'] = list_id - @message.header['List-Archive'] = topic.url if topic + + if topic + if SiteSetting.private_email? + @message.header['List-Archive'] = "#{Discourse.base_url}#{topic.slugless_url}" + else + @message.header['List-Archive'] = topic.url + end + end end if reply_key.present? && @message.header['Reply-To'] =~ /\<([^\>]+)\>/ diff --git a/lib/es6_module_transpiler/sprockets.rb b/lib/es6_module_transpiler/sprockets.rb index 1a62c5cbfb..9da657f03c 100644 --- a/lib/es6_module_transpiler/sprockets.rb +++ b/lib/es6_module_transpiler/sprockets.rb @@ -1,3 +1,4 @@ require 'sprockets' -Sprockets.register_engine '.es6', Tilt::ES6ModuleTranspilerTemplate +Sprockets.register_mime_type 'application/ecmascript6', extensions: ['.es6', '.js.es6'], charset: :unicode +Sprockets.register_transformer 'application/ecmascript6', 'application/javascript', Tilt::ES6ModuleTranspilerTemplate diff --git a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb index d18afbf3c9..f093a0a30e 100644 --- a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb +++ b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb @@ -10,6 +10,15 @@ module Tilt @mutex = Mutex.new @ctx_init = Mutex.new + def self.call(input) + filename = input[:filename] + source = input[:data] + context = input[:environment].context_class.new(input) + + result = new(filename){source}.render(context) + context.metadata.merge(data: result) + end + def prepare # intentionally left empty # Tilt requires this method to be defined diff --git a/lib/excerpt_parser.rb b/lib/excerpt_parser.rb index c162bd89be..b6547b59bb 100644 --- a/lib/excerpt_parser.rb +++ b/lib/excerpt_parser.rb @@ -68,9 +68,9 @@ class ExcerptParser < Nokogiri::XML::SAX::Document # If include_images is set, include the image in markdown characters("!") if @markdown_images - if attributes["alt"] + if !attributes["alt"].blank? characters("[#{attributes["alt"]}]") - elsif attributes["title"] + elsif !attributes["title"].blank? characters("[#{attributes["title"]}]") else characters("[#{I18n.t 'excerpt_image'}]") @@ -85,7 +85,11 @@ class ExcerptParser < Nokogiri::XML::SAX::Document end when "aside" - @in_quote = true unless @keep_onebox_source + attributes = Hash[*attributes.flatten] + + unless @keep_onebox_source && attributes['class'].include?('onebox') + @in_quote = true + end when 'article' if @keep_onebox_source && attributes.include?(['class', 'onebox-body']) @in_quote = true diff --git a/lib/freedom_patches/resolve.rb b/lib/freedom_patches/resolve.rb deleted file mode 100644 index f4fdb619c7..0000000000 --- a/lib/freedom_patches/resolve.rb +++ /dev/null @@ -1,19 +0,0 @@ -# sass-rails expects an actual file to exists when calling `@import`. However, -# we don't actually create the files for our special imports but rather inject -# them dynamically. -module Discourse - module Sprockets - module Resolve - def resolve(path, options = {}) - return [path, []] if DiscourseSassImporter.special_imports.has_key?(File.basename(path, '.scss')) - super - end - end - end -end - -module Sprockets - class Base - prepend Discourse::Sprockets::Resolve - end -end diff --git a/lib/git_importer.rb b/lib/git_importer.rb new file mode 100644 index 0000000000..e7c175e267 --- /dev/null +++ b/lib/git_importer.rb @@ -0,0 +1,49 @@ +class GitImporter + + attr_reader :url + + def initialize(url) + @url = url + if @url.start_with?("https://github.com") && !@url.end_with?(".git") + @url += ".git" + end + @temp_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_#{SecureRandom.hex}" + end + + def import! + Discourse::Utils.execute_command("git", "clone", @url, @temp_folder) + end + + def commits_since(hash) + commit_hash, commits_behind = nil + + Dir.chdir(@temp_folder) do + commit_hash = Discourse::Utils.execute_command("git", "rev-parse", "HEAD").strip + commits_behind = Discourse::Utils.execute_command("git", "rev-list", "#{hash}..HEAD", "--count").strip + end + + [commit_hash, commits_behind] + end + + def version + Dir.chdir(@temp_folder) do + Discourse::Utils.execute_command("git", "rev-parse", "HEAD").strip + end + end + + def cleanup! + FileUtils.rm_rf(@temp_folder) + end + + def [](value) + fullpath = "#{@temp_folder}/#{value}" + return nil unless File.exist?(fullpath) + + # careful to handle symlinks here, don't want to expose random data + fullpath = Pathname.new(fullpath).realpath.to_s + if fullpath && fullpath.start_with?(@temp_folder) + File.read(fullpath) + end + end + +end diff --git a/lib/guardian.rb b/lib/guardian.rb index 4a36993884..48af49e63d 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -159,7 +159,7 @@ class Guardian end def can_view_action_logs?(target) - is_staff? && target + target.present? && is_staff? end # Can we approve it? @@ -297,6 +297,14 @@ class Guardian UserExport.where(user_id: @user.id, created_at: (Time.zone.now.beginning_of_day..Time.zone.now.end_of_day)).count == 0 end + def allow_theme?(theme_key) + if is_staff? + Theme.theme_keys.include?(theme_key) + else + Theme.user_theme_keys.include?(theme_key) + end + end + private diff --git a/lib/html_to_markdown.rb b/lib/html_to_markdown.rb new file mode 100644 index 0000000000..6a5112c827 --- /dev/null +++ b/lib/html_to_markdown.rb @@ -0,0 +1,196 @@ +require "nokogiri" + +class HtmlToMarkdown + + class Block < Struct.new(:name, :head, :body, :opened, :markdown) + def initialize(name, head="", body="", opened=false, markdown=""); super; end + end + + def initialize(html) + @doc = Nokogiri::HTML.fragment(html) + remove_whitespaces! + end + + def remove_whitespaces! + @doc.traverse do |node| + if node.is_a? Nokogiri::XML::Text + node.content = node.content.lstrip if node.previous_element&.description&.block? + node.content = node.content.lstrip if node.previous_element.nil? && node.parent.description&.block? + node.content = node.content.rstrip if node.next_element&.description&.block? + node.content = node.content.rstrip if node.next_element.nil? && node.parent.description&.block? + node.remove if node.content.empty? + end + end + end + + def to_markdown + @stack = [Block.new("root")] + @markdown = "" + traverse(@doc) + @markdown << format_block + @markdown.gsub(/\n{3,}/, "\n\n").strip + end + + def traverse(node) + node.children.each { |node| visit(node) } + end + + def visit(node) + if node.description&.block? && node.parent&.description&.block? && @stack[-1].markdown.size > 0 + block = @stack[-1].dup + @markdown << format_block + block.markdown = "" + block.opened = true + @stack << block + end + + visitor = "visit_#{node.name}" + respond_to?(visitor) ? send(visitor, node) : traverse(node) + end + + BLACKLISTED ||= %w{button datalist fieldset form input label legend meter optgroup option output progress select textarea style script} + BLACKLISTED.each do |tag| + class_eval <<-RUBY + def visit_#{tag}(node) + "" + end + RUBY + end + + def visit_pre(node) + code = node.children.find { |c| c.name == "code" } + code_class = code ? code["class"] : "" + lang = code_class ? code_class[/lang-(\w+)/, 1] : "" + @stack << Block.new("pre") + @markdown << "```#{lang}\n" + traverse(node) + @markdown << format_block + @markdown << "```\n" + end + + def visit_blockquote(node) + @stack << Block.new("blockquote", "> ", "> ") + traverse(node) + @markdown << format_block + end + + BLOCK_WITH_NEWLINE ||= %w{div p} + BLOCK_WITH_NEWLINE.each do |tag| + class_eval <<-RUBY + def visit_#{tag}(node) + @stack << Block.new("#{tag}") + traverse(node) + @markdown << format_block + @markdown << "\n" + end + RUBY + end + + BLOCK_LIST ||= %w{menu ol ul} + BLOCK_LIST.each do |tag| + class_eval <<-RUBY + def visit_#{tag}(node) + @stack << Block.new("#{tag}") + traverse(node) + @markdown << format_block + end + RUBY + end + + def visit_li(node) + parent = @stack.reverse.find { |n| n.name[/ul|ol|menu/] } + prefix = parent.name == "ol" ? "1. " : "- " + @stack << Block.new("li", prefix, " ") + traverse(node) + @markdown << format_block + end + + (1..6).each do |n| + class_eval <<-RUBY + def visit_h#{n}(node) + @stack << Block.new("h#{n}", "#" * #{n} + " ") + traverse(node) + @markdown << format_block + end + RUBY + end + + WHITELISTED ||= %w{del ins kbd s small strike sub sup} + WHITELISTED.each do |tag| + class_eval <<-RUBY + def visit_#{tag}(node) + @stack[-1].markdown << "<#{tag}>" + traverse(node) + @stack[-1].markdown << "" + end + RUBY + end + + def visit_abbr(node) + @stack[-1].markdown << (node["title"].present? ? %Q[] : "") + traverse(node) + @stack[-1].markdown << "" + end + + def visit_img(node) + title = node["alt"].presence || node["title"].presence + @stack[-1].markdown << "![#{title}](#{node["src"]})" + end + + def visit_a(node) + @stack[-1].markdown << "[" + traverse(node) + @stack[-1].markdown << "](#{node["href"]})" + end + + def visit_tt(node) + @stack[-1].markdown << "`" + traverse(node) + @stack[-1].markdown << "`" + end + + def visit_code(node) + @stack.reverse.find { |n| n.name["pre"] } ? traverse(node) : visit_tt(node) + end + + def visit_br(node) + @stack[-1].markdown << "\n" + end + + def visit_hr(node) + @stack[-1].markdown << "\n\n---\n\n" + end + + def visit_strong(node) + delimiter = node.text["*"] ? "__" : "**" + @stack[-1].markdown << delimiter + traverse(node) + @stack[-1].markdown << delimiter + end + + alias :visit_b :visit_strong + + def visit_em(node) + delimiter = node.text["*"] ? "_" : "*" + @stack[-1].markdown << delimiter + traverse(node) + @stack[-1].markdown << delimiter + end + + alias :visit_i :visit_em + + def visit_text(node) + @stack[-1].markdown << node.text.gsub(/\s{2,}/, " ") + end + + def format_block + lines = @stack[-1].markdown.each_line.map do |line| + prefix = @stack.map { |b| b.opened ? b.body : b.head }.join + @stack.each { |b| b.opened = true } + prefix + line.rstrip + end + @stack.pop + (lines + [""]).join("\n") + end + +end diff --git a/lib/javascripts/moment.js b/lib/javascripts/moment.js index 7f1d25a4c5..a9947fb693 100644 --- a/lib/javascripts/moment.js +++ b/lib/javascripts/moment.js @@ -1,5 +1,5 @@ //! moment.js -//! version : 2.13.0 +//! version : 2.18.1 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com @@ -8,2408 +8,3369 @@ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.moment = factory() -}(this, function () { 'use strict'; +}(this, (function () { 'use strict'; - var hookCallback; +var hookCallback; - function utils_hooks__hooks () { - return hookCallback.apply(null, arguments); +function hooks () { + return hookCallback.apply(null, arguments); +} + +// This is done to register the method called with moment() +// without creating circular dependencies. +function setHookCallback (callback) { + hookCallback = callback; +} + +function isArray(input) { + return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; +} + +function isObject(input) { + // IE8 will treat undefined and null as object if it wasn't for + // input != null + return input != null && Object.prototype.toString.call(input) === '[object Object]'; +} + +function isObjectEmpty(obj) { + var k; + for (k in obj) { + // even if its not own property I'd still call it non-empty + return false; } + return true; +} - // This is done to register the method called with moment() - // without creating circular dependencies. - function setHookCallback (callback) { - hookCallback = callback; +function isUndefined(input) { + return input === void 0; +} + +function isNumber(input) { + return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; +} + +function isDate(input) { + return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; +} + +function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); } + return res; +} - function isArray(input) { - return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; - } +function hasOwnProp(a, b) { + return Object.prototype.hasOwnProperty.call(a, b); +} - function isDate(input) { - return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; - } - - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); +function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; } - return res; } - function hasOwnProp(a, b) { - return Object.prototype.hasOwnProperty.call(a, b); + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; } - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } + + return a; +} + +function createUTC (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, true).utc(); +} + +function defaultParsingFlags() { + // We need to deep clone this object. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso : false, + parsedDateParts : [], + meridiem : null, + rfc2822 : false, + weekdayMismatch : false + }; +} + +function getParsingFlags(m) { + if (m._pf == null) { + m._pf = defaultParsingFlags(); + } + return m._pf; +} + +var some; +if (Array.prototype.some) { + some = Array.prototype.some; +} else { + some = function (fun) { + var t = Object(this); + var len = t.length >>> 0; + + for (var i = 0; i < len; i++) { + if (i in t && fun.call(this, t[i], i, t)) { + return true; } } - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; + return false; + }; +} + +var some$1 = some; + +function isValid(m) { + if (m._isValid == null) { + var flags = getParsingFlags(m); + var parsedParts = some$1.call(flags.parsedDateParts, function (i) { + return i != null; + }); + var isNowValid = !isNaN(m._d.getTime()) && + flags.overflow < 0 && + !flags.empty && + !flags.invalidMonth && + !flags.invalidWeekday && + !flags.nullInput && + !flags.invalidFormat && + !flags.userInvalidated && + (!flags.meridiem || (flags.meridiem && parsedParts)); + + if (m._strict) { + isNowValid = isNowValid && + flags.charsLeftOver === 0 && + flags.unusedTokens.length === 0 && + flags.bigHour === undefined; } - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } - - return a; - } - - function create_utc__createUTC (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, true).utc(); - } - - function defaultParsingFlags() { - // We need to deep clone this object. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso : false, - parsedDateParts : [], - meridiem : null - }; - } - - function getParsingFlags(m) { - if (m._pf == null) { - m._pf = defaultParsingFlags(); - } - return m._pf; - } - - var some; - if (Array.prototype.some) { - some = Array.prototype.some; - } else { - some = function (fun) { - var t = Object(this); - var len = t.length >>> 0; - - for (var i = 0; i < len; i++) { - if (i in t && fun.call(this, t[i], i, t)) { - return true; - } - } - - return false; - }; - } - - function valid__isValid(m) { - if (m._isValid == null) { - var flags = getParsingFlags(m); - var parsedParts = some.call(flags.parsedDateParts, function (i) { - return i != null; - }); - m._isValid = !isNaN(m._d.getTime()) && - flags.overflow < 0 && - !flags.empty && - !flags.invalidMonth && - !flags.invalidWeekday && - !flags.nullInput && - !flags.invalidFormat && - !flags.userInvalidated && - (!flags.meridiem || (flags.meridiem && parsedParts)); - - if (m._strict) { - m._isValid = m._isValid && - flags.charsLeftOver === 0 && - flags.unusedTokens.length === 0 && - flags.bigHour === undefined; - } - } - return m._isValid; - } - - function valid__createInvalid (flags) { - var m = create_utc__createUTC(NaN); - if (flags != null) { - extend(getParsingFlags(m), flags); + if (Object.isFrozen == null || !Object.isFrozen(m)) { + m._isValid = isNowValid; } else { - getParsingFlags(m).userInvalidated = true; + return isNowValid; } + } + return m._isValid; +} - return m; +function createInvalid (flags) { + var m = createUTC(NaN); + if (flags != null) { + extend(getParsingFlags(m), flags); + } + else { + getParsingFlags(m).userInvalidated = true; } - function isUndefined(input) { - return input === void 0; + return m; +} + +// Plugins that add properties should also add the key here (null value), +// so we can properly clone ourselves. +var momentProperties = hooks.momentProperties = []; + +function copyConfig(to, from) { + var i, prop, val; + + if (!isUndefined(from._isAMomentObject)) { + to._isAMomentObject = from._isAMomentObject; + } + if (!isUndefined(from._i)) { + to._i = from._i; + } + if (!isUndefined(from._f)) { + to._f = from._f; + } + if (!isUndefined(from._l)) { + to._l = from._l; + } + if (!isUndefined(from._strict)) { + to._strict = from._strict; + } + if (!isUndefined(from._tzm)) { + to._tzm = from._tzm; + } + if (!isUndefined(from._isUTC)) { + to._isUTC = from._isUTC; + } + if (!isUndefined(from._offset)) { + to._offset = from._offset; + } + if (!isUndefined(from._pf)) { + to._pf = getParsingFlags(from); + } + if (!isUndefined(from._locale)) { + to._locale = from._locale; } - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - var momentProperties = utils_hooks__hooks.momentProperties = []; - - function copyConfig(to, from) { - var i, prop, val; - - if (!isUndefined(from._isAMomentObject)) { - to._isAMomentObject = from._isAMomentObject; - } - if (!isUndefined(from._i)) { - to._i = from._i; - } - if (!isUndefined(from._f)) { - to._f = from._f; - } - if (!isUndefined(from._l)) { - to._l = from._l; - } - if (!isUndefined(from._strict)) { - to._strict = from._strict; - } - if (!isUndefined(from._tzm)) { - to._tzm = from._tzm; - } - if (!isUndefined(from._isUTC)) { - to._isUTC = from._isUTC; - } - if (!isUndefined(from._offset)) { - to._offset = from._offset; - } - if (!isUndefined(from._pf)) { - to._pf = getParsingFlags(from); - } - if (!isUndefined(from._locale)) { - to._locale = from._locale; - } - - if (momentProperties.length > 0) { - for (i in momentProperties) { - prop = momentProperties[i]; - val = from[prop]; - if (!isUndefined(val)) { - to[prop] = val; - } + if (momentProperties.length > 0) { + for (i = 0; i < momentProperties.length; i++) { + prop = momentProperties[i]; + val = from[prop]; + if (!isUndefined(val)) { + to[prop] = val; } } - - return to; } - var updateInProgress = false; + return to; +} - // Moment prototype object - function Moment(config) { - copyConfig(this, config); - this._d = new Date(config._d != null ? config._d.getTime() : NaN); - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - utils_hooks__hooks.updateOffset(this); - updateInProgress = false; +var updateInProgress = false; + +// Moment prototype object +function Moment(config) { + copyConfig(this, config); + this._d = new Date(config._d != null ? config._d.getTime() : NaN); + if (!this.isValid()) { + this._d = new Date(NaN); + } + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + hooks.updateOffset(this); + updateInProgress = false; + } +} + +function isMoment (obj) { + return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); +} + +function absFloor (number) { + if (number < 0) { + // -0 -> 0 + return Math.ceil(number) || 0; + } else { + return Math.floor(number); + } +} + +function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; + + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + value = absFloor(coercedNumber); + } + + return value; +} + +// compare two arrays, return the number of differences +function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; } } + return diffs + lengthDiff; +} - function isMoment (obj) { - return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); +function warn(msg) { + if (hooks.suppressDeprecationWarnings === false && + (typeof console !== 'undefined') && console.warn) { + console.warn('Deprecation warning: ' + msg); } +} - function absFloor (number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); +function deprecate(msg, fn) { + var firstTime = true; + + return extend(function () { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(null, msg); } - } - - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; - - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - value = absFloor(coercedNumber); - } - - return value; - } - - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } - - function warn(msg) { - if (utils_hooks__hooks.suppressDeprecationWarnings === false && - (typeof console !== 'undefined') && console.warn) { - console.warn('Deprecation warning: ' + msg); - } - } - - function deprecate(msg, fn) { - var firstTime = true; - - return extend(function () { - if (utils_hooks__hooks.deprecationHandler != null) { - utils_hooks__hooks.deprecationHandler(null, msg); - } - if (firstTime) { - warn(msg + '\nArguments: ' + Array.prototype.slice.call(arguments).join(', ') + '\n' + (new Error()).stack); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } - - var deprecations = {}; - - function deprecateSimple(name, msg) { - if (utils_hooks__hooks.deprecationHandler != null) { - utils_hooks__hooks.deprecationHandler(name, msg); - } - if (!deprecations[name]) { - warn(msg); - deprecations[name] = true; - } - } - - utils_hooks__hooks.suppressDeprecationWarnings = false; - utils_hooks__hooks.deprecationHandler = null; - - function isFunction(input) { - return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; - } - - function isObject(input) { - return Object.prototype.toString.call(input) === '[object Object]'; - } - - function locale_set__set (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (isFunction(prop)) { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - this._config = config; - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source); - } - - function mergeConfigs(parentConfig, childConfig) { - var res = extend({}, parentConfig), prop; - for (prop in childConfig) { - if (hasOwnProp(childConfig, prop)) { - if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { - res[prop] = {}; - extend(res[prop], parentConfig[prop]); - extend(res[prop], childConfig[prop]); - } else if (childConfig[prop] != null) { - res[prop] = childConfig[prop]; + if (firstTime) { + var args = []; + var arg; + for (var i = 0; i < arguments.length; i++) { + arg = ''; + if (typeof arguments[i] === 'object') { + arg += '\n[' + i + '] '; + for (var key in arguments[0]) { + arg += key + ': ' + arguments[0][key] + ', '; + } + arg = arg.slice(0, -2); // Remove trailing comma and space } else { - delete res[prop]; + arg = arguments[i]; } + args.push(arg); + } + warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); +} + +var deprecations = {}; + +function deprecateSimple(name, msg) { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(name, msg); + } + if (!deprecations[name]) { + warn(msg); + deprecations[name] = true; + } +} + +hooks.suppressDeprecationWarnings = false; +hooks.deprecationHandler = null; + +function isFunction(input) { + return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; +} + +function set (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (isFunction(prop)) { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + this._config = config; + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. + // TODO: Remove "ordinalParse" fallback in next major release. + this._dayOfMonthOrdinalParseLenient = new RegExp( + (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + + '|' + (/\d{1,2}/).source); +} + +function mergeConfigs(parentConfig, childConfig) { + var res = extend({}, parentConfig), prop; + for (prop in childConfig) { + if (hasOwnProp(childConfig, prop)) { + if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { + res[prop] = {}; + extend(res[prop], parentConfig[prop]); + extend(res[prop], childConfig[prop]); + } else if (childConfig[prop] != null) { + res[prop] = childConfig[prop]; + } else { + delete res[prop]; + } + } + } + for (prop in parentConfig) { + if (hasOwnProp(parentConfig, prop) && + !hasOwnProp(childConfig, prop) && + isObject(parentConfig[prop])) { + // make sure changes to properties don't modify parent config + res[prop] = extend({}, res[prop]); + } + } + return res; +} + +function Locale(config) { + if (config != null) { + this.set(config); + } +} + +var keys; + +if (Object.keys) { + keys = Object.keys; +} else { + keys = function (obj) { + var i, res = []; + for (i in obj) { + if (hasOwnProp(obj, i)) { + res.push(i); } } return res; - } + }; +} - function Locale(config) { - if (config != null) { - this.set(config); - } - } +var keys$1 = keys; - var keys; +var defaultCalendar = { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' +}; - if (Object.keys) { - keys = Object.keys; - } else { - keys = function (obj) { - var i, res = []; - for (i in obj) { - if (hasOwnProp(obj, i)) { - res.push(i); - } - } - return res; - }; - } +function calendar (key, mom, now) { + var output = this._calendar[key] || this._calendar['sameElse']; + return isFunction(output) ? output.call(mom, now) : output; +} - // internal storage for locale config files - var locales = {}; - var globalLocale; +var defaultLongDateFormat = { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY h:mm A', + LLLL : 'dddd, MMMM D, YYYY h:mm A' +}; - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; - } - - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; - - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return null; - } - - function loadLocale(name) { - var oldLocale = null; - // TODO: Find a better way to register and load all the locales in Node - if (!locales[name] && (typeof module !== 'undefined') && - module && module.exports) { - try { - oldLocale = globalLocale._abbr; - require('./locale/' + name); - // because defineLocale currently also sets the global locale, we - // want to undo that for lazy loaded locales - locale_locales__getSetGlobalLocale(oldLocale); - } catch (e) { } - } - return locales[name]; - } - - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - function locale_locales__getSetGlobalLocale (key, values) { - var data; - if (key) { - if (isUndefined(values)) { - data = locale_locales__getLocale(key); - } - else { - data = defineLocale(key, values); - } - - if (data) { - // moment.duration._locale = moment._locale = data; - globalLocale = data; - } - } - - return globalLocale._abbr; - } - - function defineLocale (name, config) { - if (config !== null) { - config.abbr = name; - if (locales[name] != null) { - deprecateSimple('defineLocaleOverride', - 'use moment.updateLocale(localeName, config) to change ' + - 'an existing locale. moment.defineLocale(localeName, ' + - 'config) should only be used for creating a new locale'); - config = mergeConfigs(locales[name]._config, config); - } else if (config.parentLocale != null) { - if (locales[config.parentLocale] != null) { - config = mergeConfigs(locales[config.parentLocale]._config, config); - } else { - // treat as if there is no base config - deprecateSimple('parentLocaleUndefined', - 'specified parentLocale is not defined yet'); - } - } - locales[name] = new Locale(config); - - // backwards compat for now: also set the locale - locale_locales__getSetGlobalLocale(name); - - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - } - - function updateLocale(name, config) { - if (config != null) { - var locale; - if (locales[name] != null) { - config = mergeConfigs(locales[name]._config, config); - } - locale = new Locale(config); - locale.parentLocale = locales[name]; - locales[name] = locale; - - // backwards compat for now: also set the locale - locale_locales__getSetGlobalLocale(name); - } else { - // pass null for config to unupdate, useful for tests - if (locales[name] != null) { - if (locales[name].parentLocale != null) { - locales[name] = locales[name].parentLocale; - } else if (locales[name] != null) { - delete locales[name]; - } - } - } - return locales[name]; - } - - // returns locale data - function locale_locales__getLocale (key) { - var locale; - - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } - - if (!key) { - return globalLocale; - } - - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } - - return chooseLocale(key); - } - - function locale_locales__listLocales() { - return keys(locales); - } - - var aliases = {}; - - function addUnitAlias (unit, shorthand) { - var lowerCase = unit.toLowerCase(); - aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; - } - - function normalizeUnits(units) { - return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; - } - - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } - - return normalizedInput; - } - - function makeGetSet (unit, keepTime) { - return function (value) { - if (value != null) { - get_set__set(this, unit, value); - utils_hooks__hooks.updateOffset(this, keepTime); - return this; - } else { - return get_set__get(this, unit); - } - }; - } - - function get_set__get (mom, unit) { - return mom.isValid() ? - mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; - } - - function get_set__set (mom, unit, value) { - if (mom.isValid()) { - mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } - - // MOMENTS - - function getSet (units, value) { - var unit; - if (typeof units === 'object') { - for (unit in units) { - this.set(unit, units[unit]); - } - } else { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](value); - } - } - return this; - } - - function zeroFill(number, targetLength, forceSign) { - var absNumber = '' + Math.abs(number), - zerosToFill = targetLength - absNumber.length, - sign = number >= 0; - return (sign ? (forceSign ? '+' : '') : '-') + - Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; - } - - var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; - - var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; - - var formatFunctions = {}; - - var formatTokenFunctions = {}; - - // token: 'M' - // padded: ['MM', 2] - // ordinal: 'Mo' - // callback: function () { this.month() + 1 } - function addFormatToken (token, padded, ordinal, callback) { - var func = callback; - if (typeof callback === 'string') { - func = function () { - return this[callback](); - }; - } - if (token) { - formatTokenFunctions[token] = func; - } - if (padded) { - formatTokenFunctions[padded[0]] = function () { - return zeroFill(func.apply(this, arguments), padded[1], padded[2]); - }; - } - if (ordinal) { - formatTokenFunctions[ordinal] = function () { - return this.localeData().ordinal(func.apply(this, arguments), token); - }; - } - } - - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); - } - - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; - - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } - - return function (mom) { - var output = '', i; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } - - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } - - format = expandFormat(format, m.localeData()); - formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); - - return formatFunctions[format](m); - } - - function expandFormat(format, locale) { - var i = 5; - - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; - } +function longDateFormat (key) { + var format = this._longDateFormat[key], + formatUpper = this._longDateFormat[key.toUpperCase()]; + if (format || !formatUpper) { return format; } - var match1 = /\d/; // 0 - 9 - var match2 = /\d\d/; // 00 - 99 - var match3 = /\d{3}/; // 000 - 999 - var match4 = /\d{4}/; // 0000 - 9999 - var match6 = /[+-]?\d{6}/; // -999999 - 999999 - var match1to2 = /\d\d?/; // 0 - 99 - var match3to4 = /\d\d\d\d?/; // 999 - 9999 - var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 - var match1to3 = /\d{1,3}/; // 0 - 999 - var match1to4 = /\d{1,4}/; // 0 - 9999 - var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 + this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); - var matchUnsigned = /\d+/; // 0 - inf - var matchSigned = /[+-]?\d+/; // -inf - inf + return this._longDateFormat[key]; +} - var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z - var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z +var defaultInvalidDate = 'Invalid date'; - var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 +function invalidDate () { + return this._invalidDate; +} - // any word (or two) characters or numbers including two/three word month in arabic. - // includes scottish gaelic two word and hyphenated months - var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; +var defaultOrdinal = '%d'; +var defaultDayOfMonthOrdinalParse = /\d{1,2}/; +function ordinal (number) { + return this._ordinal.replace('%d', number); +} - var regexes = {}; +var defaultRelativeTime = { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + ss : '%d seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' +}; - function addRegexToken (token, regex, strictRegex) { - regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { - return (isStrict && strictRegex) ? strictRegex : regex; - }; - } +function relativeTime (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (isFunction(output)) ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); +} - function getParseRegexForToken (token, config) { - if (!hasOwnProp(regexes, token)) { - return new RegExp(unescapeFormat(token)); - } +function pastFuture (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return isFunction(format) ? format(output) : format.replace(/%s/i, output); +} - return regexes[token](config._strict, config._locale); - } +var aliases = {}; - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function unescapeFormat(s) { - return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - })); - } +function addUnitAlias (unit, shorthand) { + var lowerCase = unit.toLowerCase(); + aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; +} - function regexEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } +function normalizeUnits(units) { + return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; +} - var tokens = {}; +function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; - function addParseToken (token, callback) { - var i, func = callback; - if (typeof token === 'string') { - token = [token]; - } - if (typeof callback === 'number') { - func = function (input, array) { - array[callback] = toInt(input); - }; - } - for (i = 0; i < token.length; i++) { - tokens[token[i]] = func; + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } } } - function addWeekParseToken (token, callback) { - addParseToken(token, function (input, array, config, token) { - config._w = config._w || {}; - callback(input, config._w, config, token); - }); - } + return normalizedInput; +} - function addTimeToArrayFromToken(token, input, config) { - if (input != null && hasOwnProp(tokens, token)) { - tokens[token](input, config._a, config, token); +var priorities = {}; + +function addUnitPriority(unit, priority) { + priorities[unit] = priority; +} + +function getPrioritizedUnits(unitsObj) { + var units = []; + for (var u in unitsObj) { + units.push({unit: u, priority: priorities[u]}); + } + units.sort(function (a, b) { + return a.priority - b.priority; + }); + return units; +} + +function makeGetSet (unit, keepTime) { + return function (value) { + if (value != null) { + set$1(this, unit, value); + hooks.updateOffset(this, keepTime); + return this; + } else { + return get(this, unit); } + }; +} + +function get (mom, unit) { + return mom.isValid() ? + mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; +} + +function set$1 (mom, unit, value) { + if (mom.isValid()) { + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); } +} - var YEAR = 0; - var MONTH = 1; - var DATE = 2; - var HOUR = 3; - var MINUTE = 4; - var SECOND = 5; - var MILLISECOND = 6; - var WEEK = 7; - var WEEKDAY = 8; +// MOMENTS - var indexOf; +function stringGet (units) { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](); + } + return this; +} - if (Array.prototype.indexOf) { - indexOf = Array.prototype.indexOf; + +function stringSet (units, value) { + if (typeof units === 'object') { + units = normalizeObjectUnits(units); + var prioritized = getPrioritizedUnits(units); + for (var i = 0; i < prioritized.length; i++) { + this[prioritized[i].unit](units[prioritized[i].unit]); + } } else { - indexOf = function (o) { - // I know - var i; - for (i = 0; i < this.length; ++i) { - if (this[i] === o) { - return i; - } - } - return -1; + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](value); + } + } + return this; +} + +function zeroFill(number, targetLength, forceSign) { + var absNumber = '' + Math.abs(number), + zerosToFill = targetLength - absNumber.length, + sign = number >= 0; + return (sign ? (forceSign ? '+' : '') : '-') + + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; +} + +var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; + +var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; + +var formatFunctions = {}; + +var formatTokenFunctions = {}; + +// token: 'M' +// padded: ['MM', 2] +// ordinal: 'Mo' +// callback: function () { this.month() + 1 } +function addFormatToken (token, padded, ordinal, callback) { + var func = callback; + if (typeof callback === 'string') { + func = function () { + return this[callback](); }; } - - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + if (token) { + formatTokenFunctions[token] = func; } + if (padded) { + formatTokenFunctions[padded[0]] = function () { + return zeroFill(func.apply(this, arguments), padded[1], padded[2]); + }; + } + if (ordinal) { + formatTokenFunctions[ordinal] = function () { + return this.localeData().ordinal(func.apply(this, arguments), token); + }; + } +} - // FORMATTING +function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); +} - addFormatToken('M', ['MM', 2], 'Mo', function () { - return this.month() + 1; - }); +function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; - addFormatToken('MMM', 0, 0, function (format) { - return this.localeData().monthsShort(this, format); - }); - - addFormatToken('MMMM', 0, 0, function (format) { - return this.localeData().months(this, format); - }); - - // ALIASES - - addUnitAlias('month', 'M'); - - // PARSING - - addRegexToken('M', match1to2); - addRegexToken('MM', match1to2, match2); - addRegexToken('MMM', function (isStrict, locale) { - return locale.monthsShortRegex(isStrict); - }); - addRegexToken('MMMM', function (isStrict, locale) { - return locale.monthsRegex(isStrict); - }); - - addParseToken(['M', 'MM'], function (input, array) { - array[MONTH] = toInt(input) - 1; - }); - - addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { - var month = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (month != null) { - array[MONTH] = month; + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; } else { - getParsingFlags(config).invalidMonth = input; + array[i] = removeFormattingTokens(array[i]); } + } + + return function (mom) { + var output = '', i; + for (i = 0; i < length; i++) { + output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; + } + return output; + }; +} + +// format date using native date object +function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } + + format = expandFormat(format, m.localeData()); + formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); + + return formatFunctions[format](m); +} + +function expandFormat(format, locale) { + var i = 5; + + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } + + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + + return format; +} + +var match1 = /\d/; // 0 - 9 +var match2 = /\d\d/; // 00 - 99 +var match3 = /\d{3}/; // 000 - 999 +var match4 = /\d{4}/; // 0000 - 9999 +var match6 = /[+-]?\d{6}/; // -999999 - 999999 +var match1to2 = /\d\d?/; // 0 - 99 +var match3to4 = /\d\d\d\d?/; // 999 - 9999 +var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 +var match1to3 = /\d{1,3}/; // 0 - 999 +var match1to4 = /\d{1,4}/; // 0 - 9999 +var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 + +var matchUnsigned = /\d+/; // 0 - inf +var matchSigned = /[+-]?\d+/; // -inf - inf + +var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z +var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z + +var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 + +// any word (or two) characters or numbers including two/three word month in arabic. +// includes scottish gaelic two word and hyphenated months +var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; + + +var regexes = {}; + +function addRegexToken (token, regex, strictRegex) { + regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { + return (isStrict && strictRegex) ? strictRegex : regex; + }; +} + +function getParseRegexForToken (token, config) { + if (!hasOwnProp(regexes, token)) { + return new RegExp(unescapeFormat(token)); + } + + return regexes[token](config._strict, config._locale); +} + +// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +function unescapeFormat(s) { + return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + })); +} + +function regexEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); +} + +var tokens = {}; + +function addParseToken (token, callback) { + var i, func = callback; + if (typeof token === 'string') { + token = [token]; + } + if (isNumber(callback)) { + func = function (input, array) { + array[callback] = toInt(input); + }; + } + for (i = 0; i < token.length; i++) { + tokens[token[i]] = func; + } +} + +function addWeekParseToken (token, callback) { + addParseToken(token, function (input, array, config, token) { + config._w = config._w || {}; + callback(input, config._w, config, token); }); +} - // LOCALES - - var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/; - var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); - function localeMonths (m, format) { - return isArray(this._months) ? this._months[m.month()] : - this._months[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; +function addTimeToArrayFromToken(token, input, config) { + if (input != null && hasOwnProp(tokens, token)) { + tokens[token](input, config._a, config, token); } +} - var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); - function localeMonthsShort (m, format) { - return isArray(this._monthsShort) ? this._monthsShort[m.month()] : - this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; - } +var YEAR = 0; +var MONTH = 1; +var DATE = 2; +var HOUR = 3; +var MINUTE = 4; +var SECOND = 5; +var MILLISECOND = 6; +var WEEK = 7; +var WEEKDAY = 8; - function units_month__handleStrictParse(monthName, format, strict) { - var i, ii, mom, llc = monthName.toLocaleLowerCase(); - if (!this._monthsParse) { - // this is not used - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - for (i = 0; i < 12; ++i) { - mom = create_utc__createUTC([2000, i]); - this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); - this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); +var indexOf; + +if (Array.prototype.indexOf) { + indexOf = Array.prototype.indexOf; +} else { + indexOf = function (o) { + // I know + var i; + for (i = 0; i < this.length; ++i) { + if (this[i] === o) { + return i; } } + return -1; + }; +} - if (strict) { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } +var indexOf$1 = indexOf; + +function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); +} + +// FORMATTING + +addFormatToken('M', ['MM', 2], 'Mo', function () { + return this.month() + 1; +}); + +addFormatToken('MMM', 0, 0, function (format) { + return this.localeData().monthsShort(this, format); +}); + +addFormatToken('MMMM', 0, 0, function (format) { + return this.localeData().months(this, format); +}); + +// ALIASES + +addUnitAlias('month', 'M'); + +// PRIORITY + +addUnitPriority('month', 8); + +// PARSING + +addRegexToken('M', match1to2); +addRegexToken('MM', match1to2, match2); +addRegexToken('MMM', function (isStrict, locale) { + return locale.monthsShortRegex(isStrict); +}); +addRegexToken('MMMM', function (isStrict, locale) { + return locale.monthsRegex(isStrict); +}); + +addParseToken(['M', 'MM'], function (input, array) { + array[MONTH] = toInt(input) - 1; +}); + +addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { + var month = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (month != null) { + array[MONTH] = month; + } else { + getParsingFlags(config).invalidMonth = input; + } +}); + +// LOCALES + +var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; +var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); +function localeMonths (m, format) { + if (!m) { + return isArray(this._months) ? this._months : + this._months['standalone']; + } + return isArray(this._months) ? this._months[m.month()] : + this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; +} + +var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); +function localeMonthsShort (m, format) { + if (!m) { + return isArray(this._monthsShort) ? this._monthsShort : + this._monthsShort['standalone']; + } + return isArray(this._monthsShort) ? this._monthsShort[m.month()] : + this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; +} + +function handleStrictParse(monthName, format, strict) { + var i, ii, mom, llc = monthName.toLocaleLowerCase(); + if (!this._monthsParse) { + // this is not used + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + for (i = 0; i < 12; ++i) { + mom = createUTC([2000, i]); + this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); + this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'MMM') { + ii = indexOf$1.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; } else { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; + ii = indexOf$1.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'MMM') { + ii = indexOf$1.call(this._shortMonthsParse, llc); + if (ii !== -1) { + return ii; } + ii = indexOf$1.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf$1.call(this._longMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf$1.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; } } +} - function localeMonthsParse (monthName, format, strict) { - var i, mom, regex; +function localeMonthsParse (monthName, format, strict) { + var i, mom, regex; - if (this._monthsParseExact) { - return units_month__handleStrictParse.call(this, monthName, format, strict); - } - - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } - - // TODO: add sorting - // Sorting makes sure if one month (or abbr) is a prefix of another - // see sorting in computeMonthsParse - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = create_utc__createUTC([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } + if (this._monthsParseExact) { + return handleStrictParse.call(this, monthName, format, strict); } - // MOMENTS + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } - function setMonth (mom, value) { - var dayOfMonth; - - if (!mom.isValid()) { - // No op - return mom; + // TODO: add sorting + // Sorting makes sure if one month (or abbr) is a prefix of another + // see sorting in computeMonthsParse + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); } - - if (typeof value === 'string') { - if (/^\d+$/.test(value)) { - value = toInt(value); - } else { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } - } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } +} - dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); +// MOMENTS + +function setMonth (mom, value) { + var dayOfMonth; + + if (!mom.isValid()) { + // No op return mom; } - function getSetMonth (value) { - if (value != null) { - setMonth(this, value); - utils_hooks__hooks.updateOffset(this, true); - return this; + if (typeof value === 'string') { + if (/^\d+$/.test(value)) { + value = toInt(value); } else { - return get_set__get(this, 'Month'); + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (!isNumber(value)) { + return mom; + } } } - function getDaysInMonth () { - return daysInMonth(this.year(), this.month()); + dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; +} + +function getSetMonth (value) { + if (value != null) { + setMonth(this, value); + hooks.updateOffset(this, true); + return this; + } else { + return get(this, 'Month'); + } +} + +function getDaysInMonth () { + return daysInMonth(this.year(), this.month()); +} + +var defaultMonthsShortRegex = matchWord; +function monthsShortRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsShortStrictRegex; + } else { + return this._monthsShortRegex; + } + } else { + if (!hasOwnProp(this, '_monthsShortRegex')) { + this._monthsShortRegex = defaultMonthsShortRegex; + } + return this._monthsShortStrictRegex && isStrict ? + this._monthsShortStrictRegex : this._monthsShortRegex; + } +} + +var defaultMonthsRegex = matchWord; +function monthsRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsStrictRegex; + } else { + return this._monthsRegex; + } + } else { + if (!hasOwnProp(this, '_monthsRegex')) { + this._monthsRegex = defaultMonthsRegex; + } + return this._monthsStrictRegex && isStrict ? + this._monthsStrictRegex : this._monthsRegex; + } +} + +function computeMonthsParse () { + function cmpLenRev(a, b) { + return b.length - a.length; } - var defaultMonthsShortRegex = matchWord; - function monthsShortRegex (isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); + var shortPieces = [], longPieces = [], mixedPieces = [], + i, mom; + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + shortPieces.push(this.monthsShort(mom, '')); + longPieces.push(this.months(mom, '')); + mixedPieces.push(this.months(mom, '')); + mixedPieces.push(this.monthsShort(mom, '')); + } + // Sorting makes sure if one month (or abbr) is a prefix of another it + // will match the longer piece. + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 12; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + } + for (i = 0; i < 24; i++) { + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._monthsShortRegex = this._monthsRegex; + this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); +} + +// FORMATTING + +addFormatToken('Y', 0, 0, function () { + var y = this.year(); + return y <= 9999 ? '' + y : '+' + y; +}); + +addFormatToken(0, ['YY', 2], 0, function () { + return this.year() % 100; +}); + +addFormatToken(0, ['YYYY', 4], 0, 'year'); +addFormatToken(0, ['YYYYY', 5], 0, 'year'); +addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); + +// ALIASES + +addUnitAlias('year', 'y'); + +// PRIORITIES + +addUnitPriority('year', 1); + +// PARSING + +addRegexToken('Y', matchSigned); +addRegexToken('YY', match1to2, match2); +addRegexToken('YYYY', match1to4, match4); +addRegexToken('YYYYY', match1to6, match6); +addRegexToken('YYYYYY', match1to6, match6); + +addParseToken(['YYYYY', 'YYYYYY'], YEAR); +addParseToken('YYYY', function (input, array) { + array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); +}); +addParseToken('YY', function (input, array) { + array[YEAR] = hooks.parseTwoDigitYear(input); +}); +addParseToken('Y', function (input, array) { + array[YEAR] = parseInt(input, 10); +}); + +// HELPERS + +function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; +} + +function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; +} + +// HOOKS + +hooks.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); +}; + +// MOMENTS + +var getSetYear = makeGetSet('FullYear', true); + +function getIsLeapYear () { + return isLeapYear(this.year()); +} + +function createDate (y, m, d, h, M, s, ms) { + // can't just apply() to create a date: + // https://stackoverflow.com/q/181348 + var date = new Date(y, m, d, h, M, s, ms); + + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { + date.setFullYear(y); + } + return date; +} + +function createUTCDate (y) { + var date = new Date(Date.UTC.apply(null, arguments)); + + // the Date.UTC function remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { + date.setUTCFullYear(y); + } + return date; +} + +// start-of-first-week - start-of-year +function firstWeekOffset(year, dow, doy) { + var // first-week day -- which january is always in the first week (4 for iso, 1 for other) + fwd = 7 + dow - doy, + // first-week day local weekday -- which local weekday is fwd + fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; + + return -fwdlw + fwd - 1; +} + +// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday +function dayOfYearFromWeeks(year, week, weekday, dow, doy) { + var localWeekday = (7 + weekday - dow) % 7, + weekOffset = firstWeekOffset(year, dow, doy), + dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, + resYear, resDayOfYear; + + if (dayOfYear <= 0) { + resYear = year - 1; + resDayOfYear = daysInYear(resYear) + dayOfYear; + } else if (dayOfYear > daysInYear(year)) { + resYear = year + 1; + resDayOfYear = dayOfYear - daysInYear(year); + } else { + resYear = year; + resDayOfYear = dayOfYear; + } + + return { + year: resYear, + dayOfYear: resDayOfYear + }; +} + +function weekOfYear(mom, dow, doy) { + var weekOffset = firstWeekOffset(mom.year(), dow, doy), + week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, + resWeek, resYear; + + if (week < 1) { + resYear = mom.year() - 1; + resWeek = week + weeksInYear(resYear, dow, doy); + } else if (week > weeksInYear(mom.year(), dow, doy)) { + resWeek = week - weeksInYear(mom.year(), dow, doy); + resYear = mom.year() + 1; + } else { + resYear = mom.year(); + resWeek = week; + } + + return { + week: resWeek, + year: resYear + }; +} + +function weeksInYear(year, dow, doy) { + var weekOffset = firstWeekOffset(year, dow, doy), + weekOffsetNext = firstWeekOffset(year + 1, dow, doy); + return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; +} + +// FORMATTING + +addFormatToken('w', ['ww', 2], 'wo', 'week'); +addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); + +// ALIASES + +addUnitAlias('week', 'w'); +addUnitAlias('isoWeek', 'W'); + +// PRIORITIES + +addUnitPriority('week', 5); +addUnitPriority('isoWeek', 5); + +// PARSING + +addRegexToken('w', match1to2); +addRegexToken('ww', match1to2, match2); +addRegexToken('W', match1to2); +addRegexToken('WW', match1to2, match2); + +addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { + week[token.substr(0, 1)] = toInt(input); +}); + +// HELPERS + +// LOCALES + +function localeWeek (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; +} + +var defaultLocaleWeek = { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. +}; + +function localeFirstDayOfWeek () { + return this._week.dow; +} + +function localeFirstDayOfYear () { + return this._week.doy; +} + +// MOMENTS + +function getSetWeek (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); +} + +function getSetISOWeek (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); +} + +// FORMATTING + +addFormatToken('d', 0, 'do', 'day'); + +addFormatToken('dd', 0, 0, function (format) { + return this.localeData().weekdaysMin(this, format); +}); + +addFormatToken('ddd', 0, 0, function (format) { + return this.localeData().weekdaysShort(this, format); +}); + +addFormatToken('dddd', 0, 0, function (format) { + return this.localeData().weekdays(this, format); +}); + +addFormatToken('e', 0, 0, 'weekday'); +addFormatToken('E', 0, 0, 'isoWeekday'); + +// ALIASES + +addUnitAlias('day', 'd'); +addUnitAlias('weekday', 'e'); +addUnitAlias('isoWeekday', 'E'); + +// PRIORITY +addUnitPriority('day', 11); +addUnitPriority('weekday', 11); +addUnitPriority('isoWeekday', 11); + +// PARSING + +addRegexToken('d', match1to2); +addRegexToken('e', match1to2); +addRegexToken('E', match1to2); +addRegexToken('dd', function (isStrict, locale) { + return locale.weekdaysMinRegex(isStrict); +}); +addRegexToken('ddd', function (isStrict, locale) { + return locale.weekdaysShortRegex(isStrict); +}); +addRegexToken('dddd', function (isStrict, locale) { + return locale.weekdaysRegex(isStrict); +}); + +addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { + var weekday = config._locale.weekdaysParse(input, token, config._strict); + // if we didn't get a weekday name, mark the date as invalid + if (weekday != null) { + week.d = weekday; + } else { + getParsingFlags(config).invalidWeekday = input; + } +}); + +addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { + week[token] = toInt(input); +}); + +// HELPERS + +function parseWeekday(input, locale) { + if (typeof input !== 'string') { + return input; + } + + if (!isNaN(input)) { + return parseInt(input, 10); + } + + input = locale.weekdaysParse(input); + if (typeof input === 'number') { + return input; + } + + return null; +} + +function parseIsoWeekday(input, locale) { + if (typeof input === 'string') { + return locale.weekdaysParse(input) % 7 || 7; + } + return isNaN(input) ? null : input; +} + +// LOCALES + +var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); +function localeWeekdays (m, format) { + if (!m) { + return isArray(this._weekdays) ? this._weekdays : + this._weekdays['standalone']; + } + return isArray(this._weekdays) ? this._weekdays[m.day()] : + this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; +} + +var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); +function localeWeekdaysShort (m) { + return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; +} + +var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); +function localeWeekdaysMin (m) { + return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; +} + +function handleStrictParse$1(weekdayName, format, strict) { + var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._shortWeekdaysParse = []; + this._minWeekdaysParse = []; + + for (i = 0; i < 7; ++i) { + mom = createUTC([2000, 1]).day(i); + this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); + this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); + this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'dddd') { + ii = indexOf$1.call(this._weekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf$1.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf$1.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'dddd') { + ii = indexOf$1.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; } - if (isStrict) { - return this._monthsShortStrictRegex; + ii = indexOf$1.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf$1.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf$1.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf$1.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf$1.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf$1.call(this._minWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf$1.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf$1.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } +} + +function localeWeekdaysParse (weekdayName, format, strict) { + var i, mom, regex; + + if (this._weekdaysParseExact) { + return handleStrictParse$1.call(this, weekdayName, format, strict); + } + + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._minWeekdaysParse = []; + this._shortWeekdaysParse = []; + this._fullWeekdaysParse = []; + } + + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + + mom = createUTC([2000, 1]).day(i); + if (strict && !this._fullWeekdaysParse[i]) { + this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); + this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); + this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); + } + if (!this._weekdaysParse[i]) { + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } +} + +// MOMENTS + +function getSetDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } +} + +function getSetLocaleDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); +} + +function getSetISODayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + + if (input != null) { + var weekday = parseIsoWeekday(input, this.localeData()); + return this.day(this.day() % 7 ? weekday : weekday - 7); + } else { + return this.day() || 7; + } +} + +var defaultWeekdaysRegex = matchWord; +function weekdaysRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysStrictRegex; + } else { + return this._weekdaysRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysRegex')) { + this._weekdaysRegex = defaultWeekdaysRegex; + } + return this._weekdaysStrictRegex && isStrict ? + this._weekdaysStrictRegex : this._weekdaysRegex; + } +} + +var defaultWeekdaysShortRegex = matchWord; +function weekdaysShortRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysShortStrictRegex; + } else { + return this._weekdaysShortRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysShortRegex')) { + this._weekdaysShortRegex = defaultWeekdaysShortRegex; + } + return this._weekdaysShortStrictRegex && isStrict ? + this._weekdaysShortStrictRegex : this._weekdaysShortRegex; + } +} + +var defaultWeekdaysMinRegex = matchWord; +function weekdaysMinRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysMinStrictRegex; + } else { + return this._weekdaysMinRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysMinRegex')) { + this._weekdaysMinRegex = defaultWeekdaysMinRegex; + } + return this._weekdaysMinStrictRegex && isStrict ? + this._weekdaysMinStrictRegex : this._weekdaysMinRegex; + } +} + + +function computeWeekdaysParse () { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], + i, mom, minp, shortp, longp; + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, 1]).day(i); + minp = this.weekdaysMin(mom, ''); + shortp = this.weekdaysShort(mom, ''); + longp = this.weekdays(mom, ''); + minPieces.push(minp); + shortPieces.push(shortp); + longPieces.push(longp); + mixedPieces.push(minp); + mixedPieces.push(shortp); + mixedPieces.push(longp); + } + // Sorting makes sure if one weekday (or abbr) is a prefix of another it + // will match the longer piece. + minPieces.sort(cmpLenRev); + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 7; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._weekdaysShortRegex = this._weekdaysRegex; + this._weekdaysMinRegex = this._weekdaysRegex; + + this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); + this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); +} + +// FORMATTING + +function hFormat() { + return this.hours() % 12 || 12; +} + +function kFormat() { + return this.hours() || 24; +} + +addFormatToken('H', ['HH', 2], 0, 'hour'); +addFormatToken('h', ['hh', 2], 0, hFormat); +addFormatToken('k', ['kk', 2], 0, kFormat); + +addFormatToken('hmm', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); +}); + +addFormatToken('hmmss', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); +}); + +addFormatToken('Hmm', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2); +}); + +addFormatToken('Hmmss', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); +}); + +function meridiem (token, lowercase) { + addFormatToken(token, 0, 0, function () { + return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); + }); +} + +meridiem('a', true); +meridiem('A', false); + +// ALIASES + +addUnitAlias('hour', 'h'); + +// PRIORITY +addUnitPriority('hour', 13); + +// PARSING + +function matchMeridiem (isStrict, locale) { + return locale._meridiemParse; +} + +addRegexToken('a', matchMeridiem); +addRegexToken('A', matchMeridiem); +addRegexToken('H', match1to2); +addRegexToken('h', match1to2); +addRegexToken('k', match1to2); +addRegexToken('HH', match1to2, match2); +addRegexToken('hh', match1to2, match2); +addRegexToken('kk', match1to2, match2); + +addRegexToken('hmm', match3to4); +addRegexToken('hmmss', match5to6); +addRegexToken('Hmm', match3to4); +addRegexToken('Hmmss', match5to6); + +addParseToken(['H', 'HH'], HOUR); +addParseToken(['k', 'kk'], function (input, array, config) { + var kInput = toInt(input); + array[HOUR] = kInput === 24 ? 0 : kInput; +}); +addParseToken(['a', 'A'], function (input, array, config) { + config._isPm = config._locale.isPM(input); + config._meridiem = input; +}); +addParseToken(['h', 'hh'], function (input, array, config) { + array[HOUR] = toInt(input); + getParsingFlags(config).bigHour = true; +}); +addParseToken('hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + getParsingFlags(config).bigHour = true; +}); +addParseToken('hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + getParsingFlags(config).bigHour = true; +}); +addParseToken('Hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); +}); +addParseToken('Hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); +}); + +// LOCALES + +function localeIsPM (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); +} + +var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; +function localeMeridiem (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } +} + + +// MOMENTS + +// Setting the hour should keep the time, because the user explicitly +// specified which hour he wants. So trying to maintain the same hour (in +// a new timezone) makes sense. Adding/subtracting hours does not follow +// this rule. +var getSetHour = makeGetSet('Hours', true); + +// months +// week +// weekdays +// meridiem +var baseConfig = { + calendar: defaultCalendar, + longDateFormat: defaultLongDateFormat, + invalidDate: defaultInvalidDate, + ordinal: defaultOrdinal, + dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, + relativeTime: defaultRelativeTime, + + months: defaultLocaleMonths, + monthsShort: defaultLocaleMonthsShort, + + week: defaultLocaleWeek, + + weekdays: defaultLocaleWeekdays, + weekdaysMin: defaultLocaleWeekdaysMin, + weekdaysShort: defaultLocaleWeekdaysShort, + + meridiemParse: defaultLocaleMeridiemParse +}; + +// internal storage for locale config files +var locales = {}; +var localeFamilies = {}; +var globalLocale; + +function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; +} + +// pick the locale from the array +// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each +// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root +function chooseLocale(names) { + var i = 0, j, next, locale, split; + + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return null; +} + +function loadLocale(name) { + var oldLocale = null; + // TODO: Find a better way to register and load all the locales in Node + if (!locales[name] && (typeof module !== 'undefined') && + module && module.exports) { + try { + oldLocale = globalLocale._abbr; + require('./locale/' + name); + // because defineLocale currently also sets the global locale, we + // want to undo that for lazy loaded locales + getSetGlobalLocale(oldLocale); + } catch (e) { } + } + return locales[name]; +} + +// This function will load locale and then set the global locale. If +// no arguments are passed in, it will simply return the current global +// locale key. +function getSetGlobalLocale (key, values) { + var data; + if (key) { + if (isUndefined(values)) { + data = getLocale(key); + } + else { + data = defineLocale(key, values); + } + + if (data) { + // moment.duration._locale = moment._locale = data; + globalLocale = data; + } + } + + return globalLocale._abbr; +} + +function defineLocale (name, config) { + if (config !== null) { + var parentConfig = baseConfig; + config.abbr = name; + if (locales[name] != null) { + deprecateSimple('defineLocaleOverride', + 'use moment.updateLocale(localeName, config) to change ' + + 'an existing locale. moment.defineLocale(localeName, ' + + 'config) should only be used for creating a new locale ' + + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); + parentConfig = locales[name]._config; + } else if (config.parentLocale != null) { + if (locales[config.parentLocale] != null) { + parentConfig = locales[config.parentLocale]._config; } else { - return this._monthsShortRegex; + if (!localeFamilies[config.parentLocale]) { + localeFamilies[config.parentLocale] = []; + } + localeFamilies[config.parentLocale].push({ + name: name, + config: config + }); + return null; + } + } + locales[name] = new Locale(mergeConfigs(parentConfig, config)); + + if (localeFamilies[name]) { + localeFamilies[name].forEach(function (x) { + defineLocale(x.name, x.config); + }); + } + + // backwards compat for now: also set the locale + // make sure we set the locale AFTER all child locales have been + // created, so we won't end up with the child locale set. + getSetGlobalLocale(name); + + + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } +} + +function updateLocale(name, config) { + if (config != null) { + var locale, parentConfig = baseConfig; + // MERGE + if (locales[name] != null) { + parentConfig = locales[name]._config; + } + config = mergeConfigs(parentConfig, config); + locale = new Locale(config); + locale.parentLocale = locales[name]; + locales[name] = locale; + + // backwards compat for now: also set the locale + getSetGlobalLocale(name); + } else { + // pass null for config to unupdate, useful for tests + if (locales[name] != null) { + if (locales[name].parentLocale != null) { + locales[name] = locales[name].parentLocale; + } else if (locales[name] != null) { + delete locales[name]; } - } else { - return this._monthsShortStrictRegex && isStrict ? - this._monthsShortStrictRegex : this._monthsShortRegex; } } + return locales[name]; +} - var defaultMonthsRegex = matchWord; - function monthsRegex (isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsStrictRegex; - } else { - return this._monthsRegex; - } - } else { - return this._monthsStrictRegex && isStrict ? - this._monthsStrictRegex : this._monthsRegex; - } +// returns locale data +function getLocale (key) { + var locale; + + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; } - function computeMonthsParse () { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var shortPieces = [], longPieces = [], mixedPieces = [], - i, mom; - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = create_utc__createUTC([2000, i]); - shortPieces.push(this.monthsShort(mom, '')); - longPieces.push(this.months(mom, '')); - mixedPieces.push(this.months(mom, '')); - mixedPieces.push(this.monthsShort(mom, '')); - } - // Sorting makes sure if one month (or abbr) is a prefix of another it - // will match the longer piece. - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - for (i = 0; i < 12; i++) { - shortPieces[i] = regexEscape(shortPieces[i]); - longPieces[i] = regexEscape(longPieces[i]); - mixedPieces[i] = regexEscape(mixedPieces[i]); - } - - this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._monthsShortRegex = this._monthsRegex; - this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); - this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); + if (!key) { + return globalLocale; } - function checkOverflow (m) { - var overflow; - var a = m._a; - - if (a && getParsingFlags(m).overflow === -2) { - overflow = - a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : - a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : - a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : - a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : - a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : - a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : - -1; - - if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } - if (getParsingFlags(m)._overflowWeeks && overflow === -1) { - overflow = WEEK; - } - if (getParsingFlags(m)._overflowWeekday && overflow === -1) { - overflow = WEEKDAY; - } - - getParsingFlags(m).overflow = overflow; + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; } - - return m; + key = [key]; } - // iso 8601 regex - // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) - var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/; - var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/; + return chooseLocale(key); +} - var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; +function listLocales() { + return keys$1(locales); +} - var isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], - ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], - ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], - ['GGGG-[W]WW', /\d{4}-W\d\d/, false], - ['YYYY-DDD', /\d{4}-\d{3}/], - ['YYYY-MM', /\d{4}-\d\d/, false], - ['YYYYYYMMDD', /[+-]\d{10}/], - ['YYYYMMDD', /\d{8}/], - // YYYYMM is NOT allowed by the standard - ['GGGG[W]WWE', /\d{4}W\d{3}/], - ['GGGG[W]WW', /\d{4}W\d{2}/, false], - ['YYYYDDD', /\d{7}/] - ]; +function checkOverflow (m) { + var overflow; + var a = m._a; - // iso time formats and regexes - var isoTimes = [ - ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], - ['HH:mm:ss', /\d\d:\d\d:\d\d/], - ['HH:mm', /\d\d:\d\d/], - ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], - ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], - ['HHmmss', /\d\d\d\d\d\d/], - ['HHmm', /\d\d\d\d/], - ['HH', /\d\d/] - ]; + if (a && getParsingFlags(m).overflow === -2) { + overflow = + a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : + a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : + a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : + a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : + a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : + a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : + -1; - var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; + if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } + if (getParsingFlags(m)._overflowWeeks && overflow === -1) { + overflow = WEEK; + } + if (getParsingFlags(m)._overflowWeekday && overflow === -1) { + overflow = WEEKDAY; + } - // date from iso format - function configFromISO(config) { - var i, l, - string = config._i, - match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), - allowTime, dateFormat, timeFormat, tzFormat; + getParsingFlags(m).overflow = overflow; + } - if (match) { - getParsingFlags(config).iso = true; + return m; +} - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(match[1])) { - dateFormat = isoDates[i][0]; - allowTime = isoDates[i][2] !== false; +// iso 8601 regex +// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) +var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; +var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; + +var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; + +var isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], + ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], + ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], + ['GGGG-[W]WW', /\d{4}-W\d\d/, false], + ['YYYY-DDD', /\d{4}-\d{3}/], + ['YYYY-MM', /\d{4}-\d\d/, false], + ['YYYYYYMMDD', /[+-]\d{10}/], + ['YYYYMMDD', /\d{8}/], + // YYYYMM is NOT allowed by the standard + ['GGGG[W]WWE', /\d{4}W\d{3}/], + ['GGGG[W]WW', /\d{4}W\d{2}/, false], + ['YYYYDDD', /\d{7}/] +]; + +// iso time formats and regexes +var isoTimes = [ + ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], + ['HH:mm:ss', /\d\d:\d\d:\d\d/], + ['HH:mm', /\d\d:\d\d/], + ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], + ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], + ['HHmmss', /\d\d\d\d\d\d/], + ['HHmm', /\d\d\d\d/], + ['HH', /\d\d/] +]; + +var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; + +// date from iso format +function configFromISO(config) { + var i, l, + string = config._i, + match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), + allowTime, dateFormat, timeFormat, tzFormat; + + if (match) { + getParsingFlags(config).iso = true; + + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(match[1])) { + dateFormat = isoDates[i][0]; + allowTime = isoDates[i][2] !== false; + break; + } + } + if (dateFormat == null) { + config._isValid = false; + return; + } + if (match[3]) { + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(match[3])) { + // match[2] should be 'T' or space + timeFormat = (match[2] || ' ') + isoTimes[i][0]; break; } } - if (dateFormat == null) { + if (timeFormat == null) { config._isValid = false; return; } - if (match[3]) { - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(match[3])) { - // match[2] should be 'T' or space - timeFormat = (match[2] || ' ') + isoTimes[i][0]; - break; - } - } - if (timeFormat == null) { - config._isValid = false; - return; - } - } - if (!allowTime && timeFormat != null) { - config._isValid = false; - return; - } - if (match[4]) { - if (tzRegex.exec(match[4])) { - tzFormat = 'Z'; - } else { - config._isValid = false; - return; - } - } - config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); - configFromStringAndFormat(config); - } else { + } + if (!allowTime && timeFormat != null) { config._isValid = false; - } - } - - // date from iso format or fallback - function configFromString(config) { - var matched = aspNetJsonRegex.exec(config._i); - - if (matched !== null) { - config._d = new Date(+matched[1]); return; } - - configFromISO(config); - if (config._isValid === false) { - delete config._isValid; - utils_hooks__hooks.createFromInputFallback(config); - } - } - - utils_hooks__hooks.createFromInputFallback = deprecate( - 'moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); - - function createDate (y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); - - //the date constructor remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { - date.setFullYear(y); - } - return date; - } - - function createUTCDate (y) { - var date = new Date(Date.UTC.apply(null, arguments)); - - //the Date.UTC function remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { - date.setUTCFullYear(y); - } - return date; - } - - // FORMATTING - - addFormatToken('Y', 0, 0, function () { - var y = this.year(); - return y <= 9999 ? '' + y : '+' + y; - }); - - addFormatToken(0, ['YY', 2], 0, function () { - return this.year() % 100; - }); - - addFormatToken(0, ['YYYY', 4], 0, 'year'); - addFormatToken(0, ['YYYYY', 5], 0, 'year'); - addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); - - // ALIASES - - addUnitAlias('year', 'y'); - - // PARSING - - addRegexToken('Y', matchSigned); - addRegexToken('YY', match1to2, match2); - addRegexToken('YYYY', match1to4, match4); - addRegexToken('YYYYY', match1to6, match6); - addRegexToken('YYYYYY', match1to6, match6); - - addParseToken(['YYYYY', 'YYYYYY'], YEAR); - addParseToken('YYYY', function (input, array) { - array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input); - }); - addParseToken('YY', function (input, array) { - array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input); - }); - addParseToken('Y', function (input, array) { - array[YEAR] = parseInt(input, 10); - }); - - // HELPERS - - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } - - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; - } - - // HOOKS - - utils_hooks__hooks.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; - - // MOMENTS - - var getSetYear = makeGetSet('FullYear', true); - - function getIsLeapYear () { - return isLeapYear(this.year()); - } - - // start-of-first-week - start-of-year - function firstWeekOffset(year, dow, doy) { - var // first-week day -- which january is always in the first week (4 for iso, 1 for other) - fwd = 7 + dow - doy, - // first-week day local weekday -- which local weekday is fwd - fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; - - return -fwdlw + fwd - 1; - } - - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, dow, doy) { - var localWeekday = (7 + weekday - dow) % 7, - weekOffset = firstWeekOffset(year, dow, doy), - dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, - resYear, resDayOfYear; - - if (dayOfYear <= 0) { - resYear = year - 1; - resDayOfYear = daysInYear(resYear) + dayOfYear; - } else if (dayOfYear > daysInYear(year)) { - resYear = year + 1; - resDayOfYear = dayOfYear - daysInYear(year); - } else { - resYear = year; - resDayOfYear = dayOfYear; - } - - return { - year: resYear, - dayOfYear: resDayOfYear - }; - } - - function weekOfYear(mom, dow, doy) { - var weekOffset = firstWeekOffset(mom.year(), dow, doy), - week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, - resWeek, resYear; - - if (week < 1) { - resYear = mom.year() - 1; - resWeek = week + weeksInYear(resYear, dow, doy); - } else if (week > weeksInYear(mom.year(), dow, doy)) { - resWeek = week - weeksInYear(mom.year(), dow, doy); - resYear = mom.year() + 1; - } else { - resYear = mom.year(); - resWeek = week; - } - - return { - week: resWeek, - year: resYear - }; - } - - function weeksInYear(year, dow, doy) { - var weekOffset = firstWeekOffset(year, dow, doy), - weekOffsetNext = firstWeekOffset(year + 1, dow, doy); - return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; - } - - // Pick the first defined of two or three arguments. - function defaults(a, b, c) { - if (a != null) { - return a; - } - if (b != null) { - return b; - } - return c; - } - - function currentDateArray(config) { - // hooks is actually the exported moment object - var nowValue = new Date(utils_hooks__hooks.now()); - if (config._useUTC) { - return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; - } - return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; - } - - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function configFromArray (config) { - var i, date, input = [], currentDate, yearToUse; - - if (config._d) { - return; - } - - currentDate = currentDateArray(config); - - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); - - if (config._dayOfYear > daysInYear(yearToUse)) { - getParsingFlags(config)._overflowDayOfYear = true; + if (match[4]) { + if (tzRegex.exec(match[4])) { + tzFormat = 'Z'; + } else { + config._isValid = false; + return; } + } + config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); + configFromStringAndFormat(config); + } else { + config._isValid = false; + } +} - date = createUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); +// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 +var basicRfcRegex = /^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/; + +// date and time from ref 2822 format +function configFromRFC2822(config) { + var string, match, dayFormat, + dateFormat, timeFormat, tzFormat; + var timezones = { + ' GMT': ' +0000', + ' EDT': ' -0400', + ' EST': ' -0500', + ' CDT': ' -0500', + ' CST': ' -0600', + ' MDT': ' -0600', + ' MST': ' -0700', + ' PDT': ' -0700', + ' PST': ' -0800' + }; + var military = 'YXWVUTSRQPONZABCDEFGHIKLM'; + var timezone, timezoneIndex; + + string = config._i + .replace(/\([^\)]*\)|[\n\t]/g, ' ') // Remove comments and folding whitespace + .replace(/(\s\s+)/g, ' ') // Replace multiple-spaces with a single space + .replace(/^\s|\s$/g, ''); // Remove leading and trailing spaces + match = basicRfcRegex.exec(string); + + if (match) { + dayFormat = match[1] ? 'ddd' + ((match[1].length === 5) ? ', ' : ' ') : ''; + dateFormat = 'D MMM ' + ((match[2].length > 10) ? 'YYYY ' : 'YY '); + timeFormat = 'HH:mm' + (match[4] ? ':ss' : ''); + + // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. + if (match[1]) { // day of week given + var momentDate = new Date(match[2]); + var momentDay = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][momentDate.getDay()]; + + if (match[1].substr(0,3) !== momentDay) { + getParsingFlags(config).weekdayMismatch = true; + config._isValid = false; + return; + } } - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; + switch (match[5].length) { + case 2: // military + if (timezoneIndex === 0) { + timezone = ' +0000'; + } else { + timezoneIndex = military.indexOf(match[5][1].toUpperCase()) - 12; + timezone = ((timezoneIndex < 0) ? ' -' : ' +') + + (('' + timezoneIndex).replace(/^-?/, '0')).match(/..$/)[0] + '00'; + } + break; + case 4: // Zone + timezone = timezones[match[5]]; + break; + default: // UT or +/-9999 + timezone = timezones[' GMT']; } + match[5] = timezone; + config._i = match.splice(1).join(''); + tzFormat = ' ZZ'; + config._f = dayFormat + dateFormat + timeFormat + tzFormat; + configFromStringAndFormat(config); + getParsingFlags(config).rfc2822 = true; + } else { + config._isValid = false; + } +} - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } +// date from iso format or fallback +function configFromString(config) { + var matched = aspNetJsonRegex.exec(config._i); - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } - - config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } - - if (config._nextDay) { - config._a[HOUR] = 24; - } + if (matched !== null) { + config._d = new Date(+matched[1]); + return; } - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; + configFromISO(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; + configFromRFC2822(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year); - week = defaults(w.W, 1); - weekday = defaults(w.E, 1); - if (weekday < 1 || weekday > 7) { + // Final attempt, use Input Fallback + hooks.createFromInputFallback(config); +} + +hooks.createFromInputFallback = deprecate( + 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + + 'discouraged and will be removed in an upcoming major release. Please refer to ' + + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } +); + +// Pick the first defined of two or three arguments. +function defaults(a, b, c) { + if (a != null) { + return a; + } + if (b != null) { + return b; + } + return c; +} + +function currentDateArray(config) { + // hooks is actually the exported moment object + var nowValue = new Date(hooks.now()); + if (config._useUTC) { + return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; + } + return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; +} + +// convert an array to a date. +// the array should mirror the parameters below +// note: all values past the year are optional and will default to the lowest possible value. +// [year, month, day , hour, minute, second, millisecond] +function configFromArray (config) { + var i, date, input = [], currentDate, yearToUse; + + if (config._d) { + return; + } + + currentDate = currentDateArray(config); + + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } + + //if the day of the year is set, figure out what it is + if (config._dayOfYear != null) { + yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); + + if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { + getParsingFlags(config)._overflowDayOfYear = true; + } + + date = createUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } + + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; + } + + config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } + + if (config._nextDay) { + config._a[HOUR] = 24; + } +} + +function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; + + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; + + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); + week = defaults(w.W, 1); + weekday = defaults(w.E, 1); + if (weekday < 1 || weekday > 7) { + weekdayOverflow = true; + } + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; + + var curWeek = weekOfYear(createLocal(), dow, doy); + + weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); + + // Default to current week. + week = defaults(w.w, curWeek.week); + + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < 0 || weekday > 6) { + weekdayOverflow = true; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + if (w.e < 0 || w.e > 6) { weekdayOverflow = true; } } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; - - weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year); - week = defaults(w.w, 1); - - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < 0 || weekday > 6) { - weekdayOverflow = true; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - if (w.e < 0 || w.e > 6) { - weekdayOverflow = true; - } - } else { - // default to begining of week - weekday = dow; - } - } - if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { - getParsingFlags(config)._overflowWeeks = true; - } else if (weekdayOverflow != null) { - getParsingFlags(config)._overflowWeekday = true; - } else { - temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; + // default to begining of week + weekday = dow; } } + if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { + getParsingFlags(config)._overflowWeeks = true; + } else if (weekdayOverflow != null) { + getParsingFlags(config)._overflowWeekday = true; + } else { + temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } +} - // constant that refers to the ISO standard - utils_hooks__hooks.ISO_8601 = function () {}; +// constant that refers to the ISO standard +hooks.ISO_8601 = function () {}; - // date from string and format string - function configFromStringAndFormat(config) { - // TODO: Move this to another part of the creation flow to prevent circular deps - if (config._f === utils_hooks__hooks.ISO_8601) { - configFromISO(config); - return; +// constant that refers to the RFC 2822 form +hooks.RFC_2822 = function () {}; + +// date from string and format string +function configFromStringAndFormat(config) { + // TODO: Move this to another part of the creation flow to prevent circular deps + if (config._f === hooks.ISO_8601) { + configFromISO(config); + return; + } + if (config._f === hooks.RFC_2822) { + configFromRFC2822(config); + return; + } + config._a = []; + getParsingFlags(config).empty = true; + + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + // console.log('token', token, 'parsedInput', parsedInput, + // 'regex', getParseRegexForToken(token, config)); + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + getParsingFlags(config).unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; } - - config._a = []; - getParsingFlags(config).empty = true; - - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; - - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; - - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - // console.log('token', token, 'parsedInput', parsedInput, - // 'regex', getParseRegexForToken(token, config)); + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - getParsingFlags(config).unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; + getParsingFlags(config).empty = false; } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - getParsingFlags(config).empty = false; - } - else { - getParsingFlags(config).unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { + else { getParsingFlags(config).unusedTokens.push(token); } + addTimeToArrayFromToken(token, parsedInput, config); } - - // add remaining unparsed input length to the string - getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - getParsingFlags(config).unusedInput.push(string); - } - - // clear _12h flag if hour is <= 12 - if (getParsingFlags(config).bigHour === true && - config._a[HOUR] <= 12 && - config._a[HOUR] > 0) { - getParsingFlags(config).bigHour = undefined; - } - - getParsingFlags(config).parsedDateParts = config._a.slice(0); - getParsingFlags(config).meridiem = config._meridiem; - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); - - configFromArray(config); - checkOverflow(config); - } - - - function meridiemFixWrap (locale, hour, meridiem) { - var isPm; - - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // this is not supposed to happen - return hour; + else if (config._strict && !parsedInput) { + getParsingFlags(config).unusedTokens.push(token); } } - // date from string and array of format strings - function configFromStringAndArray(config) { - var tempConfig, - bestMoment, - - scoreToBeat, - i, - currentScore; - - if (config._f.length === 0) { - getParsingFlags(config).invalidFormat = true; - config._d = new Date(NaN); - return; - } - - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._f = config._f[i]; - configFromStringAndFormat(tempConfig); - - if (!valid__isValid(tempConfig)) { - continue; - } - - // if there is any input that was not parsed add a penalty for that format - currentScore += getParsingFlags(tempConfig).charsLeftOver; - - //or tokens - currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; - - getParsingFlags(tempConfig).score = currentScore; - - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } - - extend(config, bestMoment || tempConfig); + // add remaining unparsed input length to the string + getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + getParsingFlags(config).unusedInput.push(string); } - function configFromObject(config) { - if (config._d) { - return; - } - - var i = normalizeObjectUnits(config._i); - config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { - return obj && parseInt(obj, 10); - }); - - configFromArray(config); + // clear _12h flag if hour is <= 12 + if (config._a[HOUR] <= 12 && + getParsingFlags(config).bigHour === true && + config._a[HOUR] > 0) { + getParsingFlags(config).bigHour = undefined; } - function createFromConfig (config) { - var res = new Moment(checkOverflow(prepareConfig(config))); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } + getParsingFlags(config).parsedDateParts = config._a.slice(0); + getParsingFlags(config).meridiem = config._meridiem; + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); - return res; + configFromArray(config); + checkOverflow(config); +} + + +function meridiemFixWrap (locale, hour, meridiem) { + var isPm; + + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // this is not supposed to happen + return hour; + } +} + +// date from string and array of format strings +function configFromStringAndArray(config) { + var tempConfig, + bestMoment, + + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + getParsingFlags(config).invalidFormat = true; + config._d = new Date(NaN); + return; } - function prepareConfig (config) { - var input = config._i, - format = config._f; + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._f = config._f[i]; + configFromStringAndFormat(tempConfig); - config._locale = config._locale || locale_locales__getLocale(config._l); - - if (input === null || (format === undefined && input === '')) { - return valid__createInvalid({nullInput: true}); + if (!isValid(tempConfig)) { + continue; } - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } + // if there is any input that was not parsed add a penalty for that format + currentScore += getParsingFlags(tempConfig).charsLeftOver; - if (isMoment(input)) { - return new Moment(checkOverflow(input)); - } else if (isArray(format)) { - configFromStringAndArray(config); - } else if (format) { - configFromStringAndFormat(config); - } else if (isDate(input)) { - config._d = input; - } else { - configFromInput(config); - } + //or tokens + currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; - if (!valid__isValid(config)) { - config._d = null; - } + getParsingFlags(tempConfig).score = currentScore; - return config; - } - - function configFromInput(config) { - var input = config._i; - if (input === undefined) { - config._d = new Date(utils_hooks__hooks.now()); - } else if (isDate(input)) { - config._d = new Date(input.valueOf()); - } else if (typeof input === 'string') { - configFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - configFromArray(config); - } else if (typeof(input) === 'object') { - configFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - utils_hooks__hooks.createFromInputFallback(config); + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; } } - function createLocalOrUTC (input, format, locale, strict, isUTC) { - var c = {}; + extend(config, bestMoment || tempConfig); +} - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c._isAMomentObject = true; - c._useUTC = c._isUTC = isUTC; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - - return createFromConfig(c); +function configFromObject(config) { + if (config._d) { + return; } - function local__createLocal (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, false); - } - - var prototypeMin = deprecate( - 'moment().min is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', - function () { - var other = local__createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other < this ? this : other; - } else { - return valid__createInvalid(); - } - } - ); - - var prototypeMax = deprecate( - 'moment().max is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', - function () { - var other = local__createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other > this ? this : other; - } else { - return valid__createInvalid(); - } - } - ); - - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return local__createLocal(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (!moments[i].isValid() || moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } - - // TODO: Use [].sort instead? - function min () { - var args = [].slice.call(arguments, 0); - - return pickBy('isBefore', args); - } - - function max () { - var args = [].slice.call(arguments, 0); - - return pickBy('isAfter', args); - } - - var now = function () { - return Date.now ? Date.now() : +(new Date()); - }; - - function Duration (duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; - - this._data = {}; - - this._locale = locale_locales__getLocale(); - - this._bubble(); - } - - function isDuration (obj) { - return obj instanceof Duration; - } - - // FORMATTING - - function offset (token, separator) { - addFormatToken(token, 0, 0, function () { - var offset = this.utcOffset(); - var sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); - }); - } - - offset('Z', ':'); - offset('ZZ', ''); - - // PARSING - - addRegexToken('Z', matchShortOffset); - addRegexToken('ZZ', matchShortOffset); - addParseToken(['Z', 'ZZ'], function (input, array, config) { - config._useUTC = true; - config._tzm = offsetFromString(matchShortOffset, input); + var i = normalizeObjectUnits(config._i); + config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { + return obj && parseInt(obj, 10); }); - // HELPERS + configFromArray(config); +} - // timezone chunker - // '+10:00' > ['10', '00'] - // '-1530' > ['-15', '30'] - var chunkOffset = /([\+\-]|\d\d)/gi; - - function offsetFromString(matcher, string) { - var matches = ((string || '').match(matcher) || []); - var chunk = matches[matches.length - 1] || []; - var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; - var minutes = +(parts[1] * 60) + toInt(parts[2]); - - return parts[0] === '+' ? minutes : -minutes; +function createFromConfig (config) { + var res = new Moment(checkOverflow(prepareConfig(config))); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; } - // Return a moment from input, that is local/utc/zone equivalent to model. - function cloneWithOffset(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (isMoment(input) || isDate(input) ? input.valueOf() : local__createLocal(input).valueOf()) - res.valueOf(); - // Use low-level api, because this fn is low-level api. - res._d.setTime(res._d.valueOf() + diff); - utils_hooks__hooks.updateOffset(res, false); - return res; + return res; +} + +function prepareConfig (config) { + var input = config._i, + format = config._f; + + config._locale = config._locale || getLocale(config._l); + + if (input === null || (format === undefined && input === '')) { + return createInvalid({nullInput: true}); + } + + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } + + if (isMoment(input)) { + return new Moment(checkOverflow(input)); + } else if (isDate(input)) { + config._d = input; + } else if (isArray(format)) { + configFromStringAndArray(config); + } else if (format) { + configFromStringAndFormat(config); + } else { + configFromInput(config); + } + + if (!isValid(config)) { + config._d = null; + } + + return config; +} + +function configFromInput(config) { + var input = config._i; + if (isUndefined(input)) { + config._d = new Date(hooks.now()); + } else if (isDate(input)) { + config._d = new Date(input.valueOf()); + } else if (typeof input === 'string') { + configFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + configFromArray(config); + } else if (isObject(input)) { + configFromObject(config); + } else if (isNumber(input)) { + // from milliseconds + config._d = new Date(input); + } else { + hooks.createFromInputFallback(config); + } +} + +function createLocalOrUTC (input, format, locale, strict, isUTC) { + var c = {}; + + if (locale === true || locale === false) { + strict = locale; + locale = undefined; + } + + if ((isObject(input) && isObjectEmpty(input)) || + (isArray(input) && input.length === 0)) { + input = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c._isAMomentObject = true; + c._useUTC = c._isUTC = isUTC; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + + return createFromConfig(c); +} + +function createLocal (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, false); +} + +var prototypeMin = deprecate( + 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other < this ? this : other; } else { - return local__createLocal(input).local(); + return createInvalid(); } } +); - function getDateOffset (m) { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(m._d.getTimezoneOffset() / 15) * 15; - } - - // HOOKS - - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - utils_hooks__hooks.updateOffset = function () {}; - - // MOMENTS - - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - function getSetOffset (input, keepLocalTime) { - var offset = this._offset || 0, - localAdjust; - if (!this.isValid()) { - return input != null ? this : NaN; - } - if (input != null) { - if (typeof input === 'string') { - input = offsetFromString(matchShortOffset, input); - } else if (Math.abs(input) < 16) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = getDateOffset(this); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - utils_hooks__hooks.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; +var prototypeMax = deprecate( + 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other > this ? this : other; } else { - return this._isUTC ? offset : getDateOffset(this); + return createInvalid(); } } +); - function getSetZone (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } - - this.utcOffset(input, keepLocalTime); - - return this; - } else { - return -this.utcOffset(); +// Pick a moment m from moments so that m[fn](other) is true for all +// other. This relies on the function fn to be transitive. +// +// moments should either be an array of moment objects or an array, whose +// first element is an array of moment objects. +function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return createLocal(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (!moments[i].isValid() || moments[i][fn](res)) { + res = moments[i]; } } + return res; +} - function setOffsetToUTC (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - } +// TODO: Use [].sort instead? +function min () { + var args = [].slice.call(arguments, 0); - function setOffsetToLocal (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; + return pickBy('isBefore', args); +} - if (keepLocalTime) { - this.subtract(getDateOffset(this), 'm'); - } - } - return this; - } +function max () { + var args = [].slice.call(arguments, 0); - function setOffsetToParsedOffset () { - if (this._tzm) { - this.utcOffset(this._tzm); - } else if (typeof this._i === 'string') { - this.utcOffset(offsetFromString(matchOffset, this._i)); - } - return this; - } + return pickBy('isAfter', args); +} - function hasAlignedHourOffset (input) { - if (!this.isValid()) { +var now = function () { + return Date.now ? Date.now() : +(new Date()); +}; + +var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; + +function isDurationValid(m) { + for (var key in m) { + if (!(ordering.indexOf(key) !== -1 && (m[key] == null || !isNaN(m[key])))) { return false; } - input = input ? local__createLocal(input).utcOffset() : 0; - - return (this.utcOffset() - input) % 60 === 0; } - function isDaylightSavingTime () { - return ( - this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset() - ); + var unitHasDecimal = false; + for (var i = 0; i < ordering.length; ++i) { + if (m[ordering[i]]) { + if (unitHasDecimal) { + return false; // only allow non-integers for smallest unit + } + if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { + unitHasDecimal = true; + } + } } - function isDaylightSavingTimeShifted () { - if (!isUndefined(this._isDSTShifted)) { - return this._isDSTShifted; + return true; +} + +function isValid$1() { + return this._isValid; +} + +function createInvalid$1() { + return createDuration(NaN); +} + +function Duration (duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; + + this._isValid = isDurationValid(normalizedInput); + + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; + + this._data = {}; + + this._locale = getLocale(); + + this._bubble(); +} + +function isDuration (obj) { + return obj instanceof Duration; +} + +function absRound (number) { + if (number < 0) { + return Math.round(-1 * number) * -1; + } else { + return Math.round(number); + } +} + +// FORMATTING + +function offset (token, separator) { + addFormatToken(token, 0, 0, function () { + var offset = this.utcOffset(); + var sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); + }); +} + +offset('Z', ':'); +offset('ZZ', ''); + +// PARSING + +addRegexToken('Z', matchShortOffset); +addRegexToken('ZZ', matchShortOffset); +addParseToken(['Z', 'ZZ'], function (input, array, config) { + config._useUTC = true; + config._tzm = offsetFromString(matchShortOffset, input); +}); + +// HELPERS + +// timezone chunker +// '+10:00' > ['10', '00'] +// '-1530' > ['-15', '30'] +var chunkOffset = /([\+\-]|\d\d)/gi; + +function offsetFromString(matcher, string) { + var matches = (string || '').match(matcher); + + if (matches === null) { + return null; + } + + var chunk = matches[matches.length - 1] || []; + var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; + var minutes = +(parts[1] * 60) + toInt(parts[2]); + + return minutes === 0 ? + 0 : + parts[0] === '+' ? minutes : -minutes; +} + +// Return a moment from input, that is local/utc/zone equivalent to model. +function cloneWithOffset(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); + // Use low-level api, because this fn is low-level api. + res._d.setTime(res._d.valueOf() + diff); + hooks.updateOffset(res, false); + return res; + } else { + return createLocal(input).local(); + } +} + +function getDateOffset (m) { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(m._d.getTimezoneOffset() / 15) * 15; +} + +// HOOKS + +// This function will be called whenever a moment is mutated. +// It is intended to keep the offset in sync with the timezone. +hooks.updateOffset = function () {}; + +// MOMENTS + +// keepLocalTime = true means only change the timezone, without +// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> +// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset +// +0200, so we adjust the time as needed, to be valid. +// +// Keeping the time actually adds/subtracts (one hour) +// from the actual represented time. That is why we call updateOffset +// a second time. In case it wants us to change the offset again +// _changeInProgress == true case, then we have to adjust, because +// there is no such time in the given timezone. +function getSetOffset (input, keepLocalTime, keepMinutes) { + var offset = this._offset || 0, + localAdjust; + if (!this.isValid()) { + return input != null ? this : NaN; + } + if (input != null) { + if (typeof input === 'string') { + input = offsetFromString(matchShortOffset, input); + if (input === null) { + return this; + } + } else if (Math.abs(input) < 16 && !keepMinutes) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = getDateOffset(this); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addSubtract(this, createDuration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + hooks.updateOffset(this, true); + this._changeInProgress = null; + } + } + return this; + } else { + return this._isUTC ? offset : getDateOffset(this); + } +} + +function getSetZone (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; } - var c = {}; + this.utcOffset(input, keepLocalTime); - copyConfig(c, this); - c = prepareConfig(c); + return this; + } else { + return -this.utcOffset(); + } +} - if (c._a) { - var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a); - this._isDSTShifted = this.isValid() && - compareArrays(c._a, other.toArray()) > 0; - } else { - this._isDSTShifted = false; +function setOffsetToUTC (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); +} + +function setOffsetToLocal (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; + + if (keepLocalTime) { + this.subtract(getDateOffset(this), 'm'); } + } + return this; +} +function setOffsetToParsedOffset () { + if (this._tzm != null) { + this.utcOffset(this._tzm, false, true); + } else if (typeof this._i === 'string') { + var tZone = offsetFromString(matchOffset, this._i); + if (tZone != null) { + this.utcOffset(tZone); + } + else { + this.utcOffset(0, true); + } + } + return this; +} + +function hasAlignedHourOffset (input) { + if (!this.isValid()) { + return false; + } + input = input ? createLocal(input).utcOffset() : 0; + + return (this.utcOffset() - input) % 60 === 0; +} + +function isDaylightSavingTime () { + return ( + this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset() + ); +} + +function isDaylightSavingTimeShifted () { + if (!isUndefined(this._isDSTShifted)) { return this._isDSTShifted; } - function isLocal () { - return this.isValid() ? !this._isUTC : false; + var c = {}; + + copyConfig(c, this); + c = prepareConfig(c); + + if (c._a) { + var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); + this._isDSTShifted = this.isValid() && + compareArrays(c._a, other.toArray()) > 0; + } else { + this._isDSTShifted = false; } - function isUtcOffset () { - return this.isValid() ? this._isUTC : false; - } + return this._isDSTShifted; +} - function isUtc () { - return this.isValid() ? this._isUTC && this._offset === 0 : false; - } +function isLocal () { + return this.isValid() ? !this._isUTC : false; +} - // ASP.NET json date format regex - var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/; +function isUtcOffset () { + return this.isValid() ? this._isUTC : false; +} - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - // and further modified to allow for strings containing both week and day - var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; +function isUtc () { + return this.isValid() ? this._isUTC && this._offset === 0 : false; +} - function create__createDuration (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - diffRes; +// ASP.NET json date format regex +var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; - if (isDuration(input)) { - duration = { - ms : input._milliseconds, - d : input._days, - M : input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : 0, - d : toInt(match[DATE]) * sign, - h : toInt(match[HOUR]) * sign, - m : toInt(match[MINUTE]) * sign, - s : toInt(match[SECOND]) * sign, - ms : toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = isoRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : parseIso(match[2], sign), - M : parseIso(match[3], sign), - w : parseIso(match[4], sign), - d : parseIso(match[5], sign), - h : parseIso(match[6], sign), - m : parseIso(match[7], sign), - s : parseIso(match[8], sign) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to)); +// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html +// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere +// and further modified to allow for strings containing both week and day +var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } +function createDuration (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + diffRes; - ret = new Duration(duration); - - if (isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } - - return ret; - } - - create__createDuration.fn = Duration.prototype; - - function parseIso (inp, sign) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - } - - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; - - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; - } - - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - - return res; - } - - function momentsDifference(base, other) { - var res; - if (!(base.isValid() && other.isValid())) { - return {milliseconds: 0, months: 0}; - } - - other = cloneWithOffset(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } - - return res; - } - - function absRound (number) { - if (number < 0) { - return Math.round(-1 * number) * -1; - } else { - return Math.round(number); - } - } - - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); - tmp = val; val = period; period = tmp; - } - - val = typeof val === 'string' ? +val : val; - dur = create__createDuration(val, period); - add_subtract__addSubtract(this, dur, direction); - return this; + if (isDuration(input)) { + duration = { + ms : input._milliseconds, + d : input._days, + M : input._months }; - } - - function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = absRound(duration._days), - months = absRound(duration._months); - - if (!mom.isValid()) { - // No op - return; - } - - updateOffset = updateOffset == null ? true : updateOffset; - - if (milliseconds) { - mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); - } - if (days) { - get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding); - } - if (months) { - setMonth(mom, get_set__get(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - utils_hooks__hooks.updateOffset(mom, days || months); - } - } - - var add_subtract__add = createAdder(1, 'add'); - var add_subtract__subtract = createAdder(-1, 'subtract'); - - function moment_calendar__calendar (time, formats) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're local/utc/offset or not. - var now = time || local__createLocal(), - sod = cloneWithOffset(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - - var output = formats && (isFunction(formats[format]) ? formats[format]() : formats[format]); - - return this.format(output || this.localeData().calendar(format, this, local__createLocal(now))); - } - - function clone () { - return new Moment(this); - } - - function isAfter (input, units) { - var localInput = isMoment(input) ? input : local__createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); - if (units === 'millisecond') { - return this.valueOf() > localInput.valueOf(); + } else if (isNumber(input)) { + duration = {}; + if (key) { + duration[key] = input; } else { - return localInput.valueOf() < this.clone().startOf(units).valueOf(); + duration.milliseconds = input; } + } else if (!!(match = aspNetRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : 0, + d : toInt(match[DATE]) * sign, + h : toInt(match[HOUR]) * sign, + m : toInt(match[MINUTE]) * sign, + s : toInt(match[SECOND]) * sign, + ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match + }; + } else if (!!(match = isoRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : parseIso(match[2], sign), + M : parseIso(match[3], sign), + w : parseIso(match[4], sign), + d : parseIso(match[5], sign), + h : parseIso(match[6], sign), + m : parseIso(match[7], sign), + s : parseIso(match[8], sign) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); + + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; } - function isBefore (input, units) { - var localInput = isMoment(input) ? input : local__createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); - if (units === 'millisecond') { - return this.valueOf() < localInput.valueOf(); - } else { - return this.clone().endOf(units).valueOf() < localInput.valueOf(); - } + ret = new Duration(duration); + + if (isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; } - function isBetween (from, to, units, inclusivity) { - inclusivity = inclusivity || '()'; - return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && - (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); + return ret; +} + +createDuration.fn = Duration.prototype; +createDuration.invalid = createInvalid$1; + +function parseIso (inp, sign) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; +} + +function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; + + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; } - function isSame (input, units) { - var localInput = isMoment(input) ? input : local__createLocal(input), - inputMs; - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - return this.valueOf() === localInput.valueOf(); - } else { - inputMs = localInput.valueOf(); - return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); - } + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + + return res; +} + +function momentsDifference(base, other) { + var res; + if (!(base.isValid() && other.isValid())) { + return {milliseconds: 0, months: 0}; } - function isSameOrAfter (input, units) { - return this.isSame(input, units) || this.isAfter(input,units); + other = cloneWithOffset(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; } - function isSameOrBefore (input, units) { - return this.isSame(input, units) || this.isBefore(input,units); - } + return res; +} - function diff (input, units, asFloat) { - var that, - zoneDelta, - delta, output; - - if (!this.isValid()) { - return NaN; +// TODO: remove 'name' arg after deprecation is removed +function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); + tmp = val; val = period; period = tmp; } - that = cloneWithOffset(input, this); + val = typeof val === 'string' ? +val : val; + dur = createDuration(val, period); + addSubtract(this, dur, direction); + return this; + }; +} - if (!that.isValid()) { - return NaN; +function addSubtract (mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = absRound(duration._days), + months = absRound(duration._months); + + if (!mom.isValid()) { + // No op + return; + } + + updateOffset = updateOffset == null ? true : updateOffset; + + if (milliseconds) { + mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); + } + if (days) { + set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); + } + if (months) { + setMonth(mom, get(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + hooks.updateOffset(mom, days || months); + } +} + +var add = createAdder(1, 'add'); +var subtract = createAdder(-1, 'subtract'); + +function getCalendarFormat(myMoment, now) { + var diff = myMoment.diff(now, 'days', true); + return diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; +} + +function calendar$1 (time, formats) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're local/utc/offset or not. + var now = time || createLocal(), + sod = cloneWithOffset(now, this).startOf('day'), + format = hooks.calendarFormat(this, sod) || 'sameElse'; + + var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); + + return this.format(output || this.localeData().calendar(format, this, createLocal(now))); +} + +function clone () { + return new Moment(this); +} + +function isAfter (input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() > localInput.valueOf(); + } else { + return localInput.valueOf() < this.clone().startOf(units).valueOf(); + } +} + +function isBefore (input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() < localInput.valueOf(); + } else { + return this.clone().endOf(units).valueOf() < localInput.valueOf(); + } +} + +function isBetween (from, to, units, inclusivity) { + inclusivity = inclusivity || '()'; + return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && + (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); +} + +function isSame (input, units) { + var localInput = isMoment(input) ? input : createLocal(input), + inputMs; + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() === localInput.valueOf(); + } else { + inputMs = localInput.valueOf(); + return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); + } +} + +function isSameOrAfter (input, units) { + return this.isSame(input, units) || this.isAfter(input,units); +} + +function isSameOrBefore (input, units) { + return this.isSame(input, units) || this.isBefore(input,units); +} + +function diff (input, units, asFloat) { + var that, + zoneDelta, + delta, output; + + if (!this.isValid()) { + return NaN; + } + + that = cloneWithOffset(input, this); + + if (!that.isValid()) { + return NaN; + } + + zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; + + units = normalizeUnits(units); + + if (units === 'year' || units === 'month' || units === 'quarter') { + output = monthDiff(this, that); + if (units === 'quarter') { + output = output / 3; + } else if (units === 'year') { + output = output / 12; } + } else { + delta = this - that; + output = units === 'second' ? delta / 1e3 : // 1000 + units === 'minute' ? delta / 6e4 : // 1000 * 60 + units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + delta; + } + return asFloat ? output : absFloor(output); +} - zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; +function monthDiff (a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; - units = normalizeUnits(units); + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } - if (units === 'year' || units === 'month' || units === 'quarter') { - output = monthDiff(this, that); - if (units === 'quarter') { - output = output / 3; - } else if (units === 'year') { - output = output / 12; - } - } else { - delta = this - that; - output = units === 'second' ? delta / 1e3 : // 1000 - units === 'minute' ? delta / 6e4 : // 1000 * 60 - units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - delta; + //check for negative zero, return zero if negative zero + return -(wholeMonthDiff + adjust) || 0; +} + +hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; +hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; + +function toString () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); +} + +function toISOString() { + if (!this.isValid()) { + return null; + } + var m = this.clone().utc(); + if (m.year() < 0 || m.year() > 9999) { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + if (isFunction(Date.prototype.toISOString)) { + // native implementation is ~50x faster, use it when we can + return this.toDate().toISOString(); + } + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); +} + +/** + * Return a human readable representation of a moment that can + * also be evaluated to get a new moment which is the same + * + * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects + */ +function inspect () { + if (!this.isValid()) { + return 'moment.invalid(/* ' + this._i + ' */)'; + } + var func = 'moment'; + var zone = ''; + if (!this.isLocal()) { + func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; + zone = 'Z'; + } + var prefix = '[' + func + '("]'; + var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; + var datetime = '-MM-DD[T]HH:mm:ss.SSS'; + var suffix = zone + '[")]'; + + return this.format(prefix + year + datetime + suffix); +} + +function format (inputString) { + if (!inputString) { + inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; + } + var output = formatMoment(this, inputString); + return this.localeData().postformat(output); +} + +function from (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + createLocal(time).isValid())) { + return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } +} + +function fromNow (withoutSuffix) { + return this.from(createLocal(), withoutSuffix); +} + +function to (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + createLocal(time).isValid())) { + return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } +} + +function toNow (withoutSuffix) { + return this.to(createLocal(), withoutSuffix); +} + +// If passed a locale key, it will set the locale for this +// instance. Otherwise, it will return the locale configuration +// variables for this instance. +function locale (key) { + var newLocaleData; + + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = getLocale(key); + if (newLocaleData != null) { + this._locale = newLocaleData; } - return asFloat ? output : absFloor(output); + return this; } +} - function monthDiff (a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; - - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } - - //check for negative zero, return zero if negative zero - return -(wholeMonthDiff + adjust) || 0; - } - - utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; - utils_hooks__hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; - - function toString () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - } - - function moment_format__toISOString () { - var m = this.clone().utc(); - if (0 < m.year() && m.year() <= 9999) { - if (isFunction(Date.prototype.toISOString)) { - // native implementation is ~50x faster, use it when we can - return this.toDate().toISOString(); - } else { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } - - function format (inputString) { - if (!inputString) { - inputString = this.isUtc() ? utils_hooks__hooks.defaultFormatUtc : utils_hooks__hooks.defaultFormat; - } - var output = formatMoment(this, inputString); - return this.localeData().postformat(output); - } - - function from (time, withoutSuffix) { - if (this.isValid() && - ((isMoment(time) && time.isValid()) || - local__createLocal(time).isValid())) { - return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function fromNow (withoutSuffix) { - return this.from(local__createLocal(), withoutSuffix); - } - - function to (time, withoutSuffix) { - if (this.isValid() && - ((isMoment(time) && time.isValid()) || - local__createLocal(time).isValid())) { - return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function toNow (withoutSuffix) { - return this.to(local__createLocal(), withoutSuffix); - } - - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - function locale (key) { - var newLocaleData; - +var lang = deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { if (key === undefined) { - return this._locale._abbr; + return this.localeData(); } else { - newLocaleData = locale_locales__getLocale(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; + return this.locale(key); } } +); - var lang = deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ); +function localeData () { + return this._locale; +} - function localeData () { - return this._locale; - } - - function startOf (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { +function startOf (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { case 'year': this.month(0); /* falls through */ @@ -2431,1607 +3392,1072 @@ /* falls through */ case 'second': this.milliseconds(0); - } + } - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } - if (units === 'isoWeek') { - this.isoWeekday(1); - } + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } + if (units === 'isoWeek') { + this.isoWeekday(1); + } - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } + return this; +} + +function endOf (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { return this; } - function endOf (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; - } - - // 'date' is an alias for 'day', so it should be considered as such. - if (units === 'date') { - units = 'day'; - } - - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); + // 'date' is an alias for 'day', so it should be considered as such. + if (units === 'date') { + units = 'day'; } - function to_type__valueOf () { - return this._d.valueOf() - ((this._offset || 0) * 60000); - } + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); +} - function unix () { - return Math.floor(this.valueOf() / 1000); - } +function valueOf () { + return this._d.valueOf() - ((this._offset || 0) * 60000); +} - function toDate () { - return this._offset ? new Date(this.valueOf()) : this._d; - } +function unix () { + return Math.floor(this.valueOf() / 1000); +} - function toArray () { - var m = this; - return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; - } +function toDate () { + return new Date(this.valueOf()); +} - function toObject () { - var m = this; - return { - years: m.year(), - months: m.month(), - date: m.date(), - hours: m.hours(), - minutes: m.minutes(), - seconds: m.seconds(), - milliseconds: m.milliseconds() - }; - } +function toArray () { + var m = this; + return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; +} - function toJSON () { - // new Date(NaN).toJSON() === null - return this.isValid() ? this.toISOString() : null; - } - - function moment_valid__isValid () { - return valid__isValid(this); - } - - function parsingFlags () { - return extend({}, getParsingFlags(this)); - } - - function invalidAt () { - return getParsingFlags(this).overflow; - } - - function creationData() { - return { - input: this._i, - format: this._f, - locale: this._locale, - isUTC: this._isUTC, - strict: this._strict - }; - } - - // FORMATTING - - addFormatToken(0, ['gg', 2], 0, function () { - return this.weekYear() % 100; - }); - - addFormatToken(0, ['GG', 2], 0, function () { - return this.isoWeekYear() % 100; - }); - - function addWeekYearFormatToken (token, getter) { - addFormatToken(0, [token, token.length], 0, getter); - } - - addWeekYearFormatToken('gggg', 'weekYear'); - addWeekYearFormatToken('ggggg', 'weekYear'); - addWeekYearFormatToken('GGGG', 'isoWeekYear'); - addWeekYearFormatToken('GGGGG', 'isoWeekYear'); - - // ALIASES - - addUnitAlias('weekYear', 'gg'); - addUnitAlias('isoWeekYear', 'GG'); - - // PARSING - - addRegexToken('G', matchSigned); - addRegexToken('g', matchSigned); - addRegexToken('GG', match1to2, match2); - addRegexToken('gg', match1to2, match2); - addRegexToken('GGGG', match1to4, match4); - addRegexToken('gggg', match1to4, match4); - addRegexToken('GGGGG', match1to6, match6); - addRegexToken('ggggg', match1to6, match6); - - addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { - week[token.substr(0, 2)] = toInt(input); - }); - - addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { - week[token] = utils_hooks__hooks.parseTwoDigitYear(input); - }); - - // MOMENTS - - function getSetWeekYear (input) { - return getSetWeekYearHelper.call(this, - input, - this.week(), - this.weekday(), - this.localeData()._week.dow, - this.localeData()._week.doy); - } - - function getSetISOWeekYear (input) { - return getSetWeekYearHelper.call(this, - input, this.isoWeek(), this.isoWeekday(), 1, 4); - } - - function getISOWeeksInYear () { - return weeksInYear(this.year(), 1, 4); - } - - function getWeeksInYear () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - } - - function getSetWeekYearHelper(input, week, weekday, dow, doy) { - var weeksTarget; - if (input == null) { - return weekOfYear(this, dow, doy).year; - } else { - weeksTarget = weeksInYear(input, dow, doy); - if (week > weeksTarget) { - week = weeksTarget; - } - return setWeekAll.call(this, input, week, weekday, dow, doy); - } - } - - function setWeekAll(weekYear, week, weekday, dow, doy) { - var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), - date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); - - this.year(date.getUTCFullYear()); - this.month(date.getUTCMonth()); - this.date(date.getUTCDate()); - return this; - } - - // FORMATTING - - addFormatToken('Q', 0, 'Qo', 'quarter'); - - // ALIASES - - addUnitAlias('quarter', 'Q'); - - // PARSING - - addRegexToken('Q', match1); - addParseToken('Q', function (input, array) { - array[MONTH] = (toInt(input) - 1) * 3; - }); - - // MOMENTS - - function getSetQuarter (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - } - - // FORMATTING - - addFormatToken('w', ['ww', 2], 'wo', 'week'); - addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); - - // ALIASES - - addUnitAlias('week', 'w'); - addUnitAlias('isoWeek', 'W'); - - // PARSING - - addRegexToken('w', match1to2); - addRegexToken('ww', match1to2, match2); - addRegexToken('W', match1to2); - addRegexToken('WW', match1to2, match2); - - addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { - week[token.substr(0, 1)] = toInt(input); - }); - - // HELPERS - - // LOCALES - - function localeWeek (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - } - - var defaultLocaleWeek = { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. +function toObject () { + var m = this; + return { + years: m.year(), + months: m.month(), + date: m.date(), + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: m.milliseconds() }; - - function localeFirstDayOfWeek () { - return this._week.dow; - } - - function localeFirstDayOfYear () { - return this._week.doy; - } - - // MOMENTS - - function getSetWeek (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - function getSetISOWeek (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - // FORMATTING - - addFormatToken('D', ['DD', 2], 'Do', 'date'); - - // ALIASES - - addUnitAlias('date', 'D'); - - // PARSING - - addRegexToken('D', match1to2); - addRegexToken('DD', match1to2, match2); - addRegexToken('Do', function (isStrict, locale) { - return isStrict ? locale._ordinalParse : locale._ordinalParseLenient; - }); - - addParseToken(['D', 'DD'], DATE); - addParseToken('Do', function (input, array) { - array[DATE] = toInt(input.match(match1to2)[0], 10); - }); - - // MOMENTS - - var getSetDayOfMonth = makeGetSet('Date', true); - - // FORMATTING - - addFormatToken('d', 0, 'do', 'day'); - - addFormatToken('dd', 0, 0, function (format) { - return this.localeData().weekdaysMin(this, format); - }); - - addFormatToken('ddd', 0, 0, function (format) { - return this.localeData().weekdaysShort(this, format); - }); - - addFormatToken('dddd', 0, 0, function (format) { - return this.localeData().weekdays(this, format); - }); - - addFormatToken('e', 0, 0, 'weekday'); - addFormatToken('E', 0, 0, 'isoWeekday'); - - // ALIASES - - addUnitAlias('day', 'd'); - addUnitAlias('weekday', 'e'); - addUnitAlias('isoWeekday', 'E'); - - // PARSING - - addRegexToken('d', match1to2); - addRegexToken('e', match1to2); - addRegexToken('E', match1to2); - addRegexToken('dd', function (isStrict, locale) { - return locale.weekdaysMinRegex(isStrict); - }); - addRegexToken('ddd', function (isStrict, locale) { - return locale.weekdaysShortRegex(isStrict); - }); - addRegexToken('dddd', function (isStrict, locale) { - return locale.weekdaysRegex(isStrict); - }); - - addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { - var weekday = config._locale.weekdaysParse(input, token, config._strict); - // if we didn't get a weekday name, mark the date as invalid - if (weekday != null) { - week.d = weekday; - } else { - getParsingFlags(config).invalidWeekday = input; - } - }); - - addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { - week[token] = toInt(input); - }); - - // HELPERS - - function parseWeekday(input, locale) { - if (typeof input !== 'string') { - return input; - } - - if (!isNaN(input)) { - return parseInt(input, 10); - } - - input = locale.weekdaysParse(input); - if (typeof input === 'number') { - return input; - } - - return null; - } - - // LOCALES - - var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); - function localeWeekdays (m, format) { - return isArray(this._weekdays) ? this._weekdays[m.day()] : - this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; - } - - var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); - function localeWeekdaysShort (m) { - return this._weekdaysShort[m.day()]; - } - - var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); - function localeWeekdaysMin (m) { - return this._weekdaysMin[m.day()]; - } - - function day_of_week__handleStrictParse(weekdayName, format, strict) { - var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._shortWeekdaysParse = []; - this._minWeekdaysParse = []; - - for (i = 0; i < 7; ++i) { - mom = create_utc__createUTC([2000, 1]).day(i); - this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); - this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); - this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); - } - } - - if (strict) { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } - } - - function localeWeekdaysParse (weekdayName, format, strict) { - var i, mom, regex; - - if (this._weekdaysParseExact) { - return day_of_week__handleStrictParse.call(this, weekdayName, format, strict); - } - - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._minWeekdaysParse = []; - this._shortWeekdaysParse = []; - this._fullWeekdaysParse = []; - } - - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - - mom = create_utc__createUTC([2000, 1]).day(i); - if (strict && !this._fullWeekdaysParse[i]) { - this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); - this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); - this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); - } - if (!this._weekdaysParse[i]) { - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - } - - // MOMENTS - - function getSetDayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - } - - function getSetLocaleDayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - } - - function getSetISODayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - } - - var defaultWeekdaysRegex = matchWord; - function weekdaysRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysStrictRegex; - } else { - return this._weekdaysRegex; - } - } else { - return this._weekdaysStrictRegex && isStrict ? - this._weekdaysStrictRegex : this._weekdaysRegex; - } - } - - var defaultWeekdaysShortRegex = matchWord; - function weekdaysShortRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysShortStrictRegex; - } else { - return this._weekdaysShortRegex; - } - } else { - return this._weekdaysShortStrictRegex && isStrict ? - this._weekdaysShortStrictRegex : this._weekdaysShortRegex; - } - } - - var defaultWeekdaysMinRegex = matchWord; - function weekdaysMinRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysMinStrictRegex; - } else { - return this._weekdaysMinRegex; - } - } else { - return this._weekdaysMinStrictRegex && isStrict ? - this._weekdaysMinStrictRegex : this._weekdaysMinRegex; - } - } - - - function computeWeekdaysParse () { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], - i, mom, minp, shortp, longp; - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - mom = create_utc__createUTC([2000, 1]).day(i); - minp = this.weekdaysMin(mom, ''); - shortp = this.weekdaysShort(mom, ''); - longp = this.weekdays(mom, ''); - minPieces.push(minp); - shortPieces.push(shortp); - longPieces.push(longp); - mixedPieces.push(minp); - mixedPieces.push(shortp); - mixedPieces.push(longp); - } - // Sorting makes sure if one weekday (or abbr) is a prefix of another it - // will match the longer piece. - minPieces.sort(cmpLenRev); - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - for (i = 0; i < 7; i++) { - shortPieces[i] = regexEscape(shortPieces[i]); - longPieces[i] = regexEscape(longPieces[i]); - mixedPieces[i] = regexEscape(mixedPieces[i]); - } - - this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._weekdaysShortRegex = this._weekdaysRegex; - this._weekdaysMinRegex = this._weekdaysRegex; - - this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); - this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); - this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); - } - - // FORMATTING - - addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); - - // ALIASES - - addUnitAlias('dayOfYear', 'DDD'); - - // PARSING - - addRegexToken('DDD', match1to3); - addRegexToken('DDDD', match3); - addParseToken(['DDD', 'DDDD'], function (input, array, config) { - config._dayOfYear = toInt(input); - }); - - // HELPERS - - // MOMENTS - - function getSetDayOfYear (input) { - var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - } - - // FORMATTING - - function hFormat() { - return this.hours() % 12 || 12; - } - - function kFormat() { - return this.hours() || 24; - } - - addFormatToken('H', ['HH', 2], 0, 'hour'); - addFormatToken('h', ['hh', 2], 0, hFormat); - addFormatToken('k', ['kk', 2], 0, kFormat); - - addFormatToken('hmm', 0, 0, function () { - return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); - }); - - addFormatToken('hmmss', 0, 0, function () { - return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2); - }); - - addFormatToken('Hmm', 0, 0, function () { - return '' + this.hours() + zeroFill(this.minutes(), 2); - }); - - addFormatToken('Hmmss', 0, 0, function () { - return '' + this.hours() + zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2); - }); - - function meridiem (token, lowercase) { - addFormatToken(token, 0, 0, function () { - return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); - }); - } - - meridiem('a', true); - meridiem('A', false); - - // ALIASES - - addUnitAlias('hour', 'h'); - - // PARSING - - function matchMeridiem (isStrict, locale) { - return locale._meridiemParse; - } - - addRegexToken('a', matchMeridiem); - addRegexToken('A', matchMeridiem); - addRegexToken('H', match1to2); - addRegexToken('h', match1to2); - addRegexToken('HH', match1to2, match2); - addRegexToken('hh', match1to2, match2); - - addRegexToken('hmm', match3to4); - addRegexToken('hmmss', match5to6); - addRegexToken('Hmm', match3to4); - addRegexToken('Hmmss', match5to6); - - addParseToken(['H', 'HH'], HOUR); - addParseToken(['a', 'A'], function (input, array, config) { - config._isPm = config._locale.isPM(input); - config._meridiem = input; - }); - addParseToken(['h', 'hh'], function (input, array, config) { - array[HOUR] = toInt(input); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmmss', function (input, array, config) { - var pos1 = input.length - 4; - var pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('Hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - }); - addParseToken('Hmmss', function (input, array, config) { - var pos1 = input.length - 4; - var pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - }); - - // LOCALES - - function localeIsPM (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - } - - var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; - function localeMeridiem (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - } - - - // MOMENTS - - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - var getSetHour = makeGetSet('Hours', true); - - // FORMATTING - - addFormatToken('m', ['mm', 2], 0, 'minute'); - - // ALIASES - - addUnitAlias('minute', 'm'); - - // PARSING - - addRegexToken('m', match1to2); - addRegexToken('mm', match1to2, match2); - addParseToken(['m', 'mm'], MINUTE); - - // MOMENTS - - var getSetMinute = makeGetSet('Minutes', false); - - // FORMATTING - - addFormatToken('s', ['ss', 2], 0, 'second'); - - // ALIASES - - addUnitAlias('second', 's'); - - // PARSING - - addRegexToken('s', match1to2); - addRegexToken('ss', match1to2, match2); - addParseToken(['s', 'ss'], SECOND); - - // MOMENTS - - var getSetSecond = makeGetSet('Seconds', false); - - // FORMATTING - - addFormatToken('S', 0, 0, function () { - return ~~(this.millisecond() / 100); - }); - - addFormatToken(0, ['SS', 2], 0, function () { - return ~~(this.millisecond() / 10); - }); - - addFormatToken(0, ['SSS', 3], 0, 'millisecond'); - addFormatToken(0, ['SSSS', 4], 0, function () { - return this.millisecond() * 10; - }); - addFormatToken(0, ['SSSSS', 5], 0, function () { - return this.millisecond() * 100; - }); - addFormatToken(0, ['SSSSSS', 6], 0, function () { - return this.millisecond() * 1000; - }); - addFormatToken(0, ['SSSSSSS', 7], 0, function () { - return this.millisecond() * 10000; - }); - addFormatToken(0, ['SSSSSSSS', 8], 0, function () { - return this.millisecond() * 100000; - }); - addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { - return this.millisecond() * 1000000; - }); - - - // ALIASES - - addUnitAlias('millisecond', 'ms'); - - // PARSING - - addRegexToken('S', match1to3, match1); - addRegexToken('SS', match1to3, match2); - addRegexToken('SSS', match1to3, match3); - - var token; - for (token = 'SSSS'; token.length <= 9; token += 'S') { - addRegexToken(token, matchUnsigned); - } - - function parseMs(input, array) { - array[MILLISECOND] = toInt(('0.' + input) * 1000); - } - - for (token = 'S'; token.length <= 9; token += 'S') { - addParseToken(token, parseMs); - } - // MOMENTS - - var getSetMillisecond = makeGetSet('Milliseconds', false); - - // FORMATTING - - addFormatToken('z', 0, 0, 'zoneAbbr'); - addFormatToken('zz', 0, 0, 'zoneName'); - - // MOMENTS - - function getZoneAbbr () { - return this._isUTC ? 'UTC' : ''; - } - - function getZoneName () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - } - - var momentPrototype__proto = Moment.prototype; - - momentPrototype__proto.add = add_subtract__add; - momentPrototype__proto.calendar = moment_calendar__calendar; - momentPrototype__proto.clone = clone; - momentPrototype__proto.diff = diff; - momentPrototype__proto.endOf = endOf; - momentPrototype__proto.format = format; - momentPrototype__proto.from = from; - momentPrototype__proto.fromNow = fromNow; - momentPrototype__proto.to = to; - momentPrototype__proto.toNow = toNow; - momentPrototype__proto.get = getSet; - momentPrototype__proto.invalidAt = invalidAt; - momentPrototype__proto.isAfter = isAfter; - momentPrototype__proto.isBefore = isBefore; - momentPrototype__proto.isBetween = isBetween; - momentPrototype__proto.isSame = isSame; - momentPrototype__proto.isSameOrAfter = isSameOrAfter; - momentPrototype__proto.isSameOrBefore = isSameOrBefore; - momentPrototype__proto.isValid = moment_valid__isValid; - momentPrototype__proto.lang = lang; - momentPrototype__proto.locale = locale; - momentPrototype__proto.localeData = localeData; - momentPrototype__proto.max = prototypeMax; - momentPrototype__proto.min = prototypeMin; - momentPrototype__proto.parsingFlags = parsingFlags; - momentPrototype__proto.set = getSet; - momentPrototype__proto.startOf = startOf; - momentPrototype__proto.subtract = add_subtract__subtract; - momentPrototype__proto.toArray = toArray; - momentPrototype__proto.toObject = toObject; - momentPrototype__proto.toDate = toDate; - momentPrototype__proto.toISOString = moment_format__toISOString; - momentPrototype__proto.toJSON = toJSON; - momentPrototype__proto.toString = toString; - momentPrototype__proto.unix = unix; - momentPrototype__proto.valueOf = to_type__valueOf; - momentPrototype__proto.creationData = creationData; - - // Year - momentPrototype__proto.year = getSetYear; - momentPrototype__proto.isLeapYear = getIsLeapYear; - - // Week Year - momentPrototype__proto.weekYear = getSetWeekYear; - momentPrototype__proto.isoWeekYear = getSetISOWeekYear; - - // Quarter - momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter; - - // Month - momentPrototype__proto.month = getSetMonth; - momentPrototype__proto.daysInMonth = getDaysInMonth; - - // Week - momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek; - momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek; - momentPrototype__proto.weeksInYear = getWeeksInYear; - momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear; - - // Day - momentPrototype__proto.date = getSetDayOfMonth; - momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek; - momentPrototype__proto.weekday = getSetLocaleDayOfWeek; - momentPrototype__proto.isoWeekday = getSetISODayOfWeek; - momentPrototype__proto.dayOfYear = getSetDayOfYear; - - // Hour - momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour; - - // Minute - momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute; - - // Second - momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond; - - // Millisecond - momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond; - - // Offset - momentPrototype__proto.utcOffset = getSetOffset; - momentPrototype__proto.utc = setOffsetToUTC; - momentPrototype__proto.local = setOffsetToLocal; - momentPrototype__proto.parseZone = setOffsetToParsedOffset; - momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset; - momentPrototype__proto.isDST = isDaylightSavingTime; - momentPrototype__proto.isDSTShifted = isDaylightSavingTimeShifted; - momentPrototype__proto.isLocal = isLocal; - momentPrototype__proto.isUtcOffset = isUtcOffset; - momentPrototype__proto.isUtc = isUtc; - momentPrototype__proto.isUTC = isUtc; - - // Timezone - momentPrototype__proto.zoneAbbr = getZoneAbbr; - momentPrototype__proto.zoneName = getZoneName; - - // Deprecations - momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); - momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); - momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); - momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779', getSetZone); - - var momentPrototype = momentPrototype__proto; - - function moment__createUnix (input) { - return local__createLocal(input * 1000); - } - - function moment__createInZone () { - return local__createLocal.apply(null, arguments).parseZone(); - } - - var defaultCalendar = { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' +} + +function toJSON () { + // new Date(NaN).toJSON() === null + return this.isValid() ? this.toISOString() : null; +} + +function isValid$2 () { + return isValid(this); +} + +function parsingFlags () { + return extend({}, getParsingFlags(this)); +} + +function invalidAt () { + return getParsingFlags(this).overflow; +} + +function creationData() { + return { + input: this._i, + format: this._f, + locale: this._locale, + isUTC: this._isUTC, + strict: this._strict }; +} - function locale_calendar__calendar (key, mom, now) { - var output = this._calendar[key]; - return isFunction(output) ? output.call(mom, now) : output; - } +// FORMATTING - var defaultLongDateFormat = { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY h:mm A', - LLLL : 'dddd, MMMM D, YYYY h:mm A' - }; +addFormatToken(0, ['gg', 2], 0, function () { + return this.weekYear() % 100; +}); - function longDateFormat (key) { - var format = this._longDateFormat[key], - formatUpper = this._longDateFormat[key.toUpperCase()]; +addFormatToken(0, ['GG', 2], 0, function () { + return this.isoWeekYear() % 100; +}); - if (format || !formatUpper) { - return format; +function addWeekYearFormatToken (token, getter) { + addFormatToken(0, [token, token.length], 0, getter); +} + +addWeekYearFormatToken('gggg', 'weekYear'); +addWeekYearFormatToken('ggggg', 'weekYear'); +addWeekYearFormatToken('GGGG', 'isoWeekYear'); +addWeekYearFormatToken('GGGGG', 'isoWeekYear'); + +// ALIASES + +addUnitAlias('weekYear', 'gg'); +addUnitAlias('isoWeekYear', 'GG'); + +// PRIORITY + +addUnitPriority('weekYear', 1); +addUnitPriority('isoWeekYear', 1); + + +// PARSING + +addRegexToken('G', matchSigned); +addRegexToken('g', matchSigned); +addRegexToken('GG', match1to2, match2); +addRegexToken('gg', match1to2, match2); +addRegexToken('GGGG', match1to4, match4); +addRegexToken('gggg', match1to4, match4); +addRegexToken('GGGGG', match1to6, match6); +addRegexToken('ggggg', match1to6, match6); + +addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { + week[token.substr(0, 2)] = toInt(input); +}); + +addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { + week[token] = hooks.parseTwoDigitYear(input); +}); + +// MOMENTS + +function getSetWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, + this.week(), + this.weekday(), + this.localeData()._week.dow, + this.localeData()._week.doy); +} + +function getSetISOWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, this.isoWeek(), this.isoWeekday(), 1, 4); +} + +function getISOWeeksInYear () { + return weeksInYear(this.year(), 1, 4); +} + +function getWeeksInYear () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); +} + +function getSetWeekYearHelper(input, week, weekday, dow, doy) { + var weeksTarget; + if (input == null) { + return weekOfYear(this, dow, doy).year; + } else { + weeksTarget = weeksInYear(input, dow, doy); + if (week > weeksTarget) { + week = weeksTarget; } + return setWeekAll.call(this, input, week, weekday, dow, doy); + } +} - this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); +function setWeekAll(weekYear, week, weekday, dow, doy) { + var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), + date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); - return this._longDateFormat[key]; + this.year(date.getUTCFullYear()); + this.month(date.getUTCMonth()); + this.date(date.getUTCDate()); + return this; +} + +// FORMATTING + +addFormatToken('Q', 0, 'Qo', 'quarter'); + +// ALIASES + +addUnitAlias('quarter', 'Q'); + +// PRIORITY + +addUnitPriority('quarter', 7); + +// PARSING + +addRegexToken('Q', match1); +addParseToken('Q', function (input, array) { + array[MONTH] = (toInt(input) - 1) * 3; +}); + +// MOMENTS + +function getSetQuarter (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); +} + +// FORMATTING + +addFormatToken('D', ['DD', 2], 'Do', 'date'); + +// ALIASES + +addUnitAlias('date', 'D'); + +// PRIOROITY +addUnitPriority('date', 9); + +// PARSING + +addRegexToken('D', match1to2); +addRegexToken('DD', match1to2, match2); +addRegexToken('Do', function (isStrict, locale) { + // TODO: Remove "ordinalParse" fallback in next major release. + return isStrict ? + (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : + locale._dayOfMonthOrdinalParseLenient; +}); + +addParseToken(['D', 'DD'], DATE); +addParseToken('Do', function (input, array) { + array[DATE] = toInt(input.match(match1to2)[0], 10); +}); + +// MOMENTS + +var getSetDayOfMonth = makeGetSet('Date', true); + +// FORMATTING + +addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); + +// ALIASES + +addUnitAlias('dayOfYear', 'DDD'); + +// PRIORITY +addUnitPriority('dayOfYear', 4); + +// PARSING + +addRegexToken('DDD', match1to3); +addRegexToken('DDDD', match3); +addParseToken(['DDD', 'DDDD'], function (input, array, config) { + config._dayOfYear = toInt(input); +}); + +// HELPERS + +// MOMENTS + +function getSetDayOfYear (input) { + var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); +} + +// FORMATTING + +addFormatToken('m', ['mm', 2], 0, 'minute'); + +// ALIASES + +addUnitAlias('minute', 'm'); + +// PRIORITY + +addUnitPriority('minute', 14); + +// PARSING + +addRegexToken('m', match1to2); +addRegexToken('mm', match1to2, match2); +addParseToken(['m', 'mm'], MINUTE); + +// MOMENTS + +var getSetMinute = makeGetSet('Minutes', false); + +// FORMATTING + +addFormatToken('s', ['ss', 2], 0, 'second'); + +// ALIASES + +addUnitAlias('second', 's'); + +// PRIORITY + +addUnitPriority('second', 15); + +// PARSING + +addRegexToken('s', match1to2); +addRegexToken('ss', match1to2, match2); +addParseToken(['s', 'ss'], SECOND); + +// MOMENTS + +var getSetSecond = makeGetSet('Seconds', false); + +// FORMATTING + +addFormatToken('S', 0, 0, function () { + return ~~(this.millisecond() / 100); +}); + +addFormatToken(0, ['SS', 2], 0, function () { + return ~~(this.millisecond() / 10); +}); + +addFormatToken(0, ['SSS', 3], 0, 'millisecond'); +addFormatToken(0, ['SSSS', 4], 0, function () { + return this.millisecond() * 10; +}); +addFormatToken(0, ['SSSSS', 5], 0, function () { + return this.millisecond() * 100; +}); +addFormatToken(0, ['SSSSSS', 6], 0, function () { + return this.millisecond() * 1000; +}); +addFormatToken(0, ['SSSSSSS', 7], 0, function () { + return this.millisecond() * 10000; +}); +addFormatToken(0, ['SSSSSSSS', 8], 0, function () { + return this.millisecond() * 100000; +}); +addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { + return this.millisecond() * 1000000; +}); + + +// ALIASES + +addUnitAlias('millisecond', 'ms'); + +// PRIORITY + +addUnitPriority('millisecond', 16); + +// PARSING + +addRegexToken('S', match1to3, match1); +addRegexToken('SS', match1to3, match2); +addRegexToken('SSS', match1to3, match3); + +var token; +for (token = 'SSSS'; token.length <= 9; token += 'S') { + addRegexToken(token, matchUnsigned); +} + +function parseMs(input, array) { + array[MILLISECOND] = toInt(('0.' + input) * 1000); +} + +for (token = 'S'; token.length <= 9; token += 'S') { + addParseToken(token, parseMs); +} +// MOMENTS + +var getSetMillisecond = makeGetSet('Milliseconds', false); + +// FORMATTING + +addFormatToken('z', 0, 0, 'zoneAbbr'); +addFormatToken('zz', 0, 0, 'zoneName'); + +// MOMENTS + +function getZoneAbbr () { + return this._isUTC ? 'UTC' : ''; +} + +function getZoneName () { + return this._isUTC ? 'Coordinated Universal Time' : ''; +} + +var proto = Moment.prototype; + +proto.add = add; +proto.calendar = calendar$1; +proto.clone = clone; +proto.diff = diff; +proto.endOf = endOf; +proto.format = format; +proto.from = from; +proto.fromNow = fromNow; +proto.to = to; +proto.toNow = toNow; +proto.get = stringGet; +proto.invalidAt = invalidAt; +proto.isAfter = isAfter; +proto.isBefore = isBefore; +proto.isBetween = isBetween; +proto.isSame = isSame; +proto.isSameOrAfter = isSameOrAfter; +proto.isSameOrBefore = isSameOrBefore; +proto.isValid = isValid$2; +proto.lang = lang; +proto.locale = locale; +proto.localeData = localeData; +proto.max = prototypeMax; +proto.min = prototypeMin; +proto.parsingFlags = parsingFlags; +proto.set = stringSet; +proto.startOf = startOf; +proto.subtract = subtract; +proto.toArray = toArray; +proto.toObject = toObject; +proto.toDate = toDate; +proto.toISOString = toISOString; +proto.inspect = inspect; +proto.toJSON = toJSON; +proto.toString = toString; +proto.unix = unix; +proto.valueOf = valueOf; +proto.creationData = creationData; + +// Year +proto.year = getSetYear; +proto.isLeapYear = getIsLeapYear; + +// Week Year +proto.weekYear = getSetWeekYear; +proto.isoWeekYear = getSetISOWeekYear; + +// Quarter +proto.quarter = proto.quarters = getSetQuarter; + +// Month +proto.month = getSetMonth; +proto.daysInMonth = getDaysInMonth; + +// Week +proto.week = proto.weeks = getSetWeek; +proto.isoWeek = proto.isoWeeks = getSetISOWeek; +proto.weeksInYear = getWeeksInYear; +proto.isoWeeksInYear = getISOWeeksInYear; + +// Day +proto.date = getSetDayOfMonth; +proto.day = proto.days = getSetDayOfWeek; +proto.weekday = getSetLocaleDayOfWeek; +proto.isoWeekday = getSetISODayOfWeek; +proto.dayOfYear = getSetDayOfYear; + +// Hour +proto.hour = proto.hours = getSetHour; + +// Minute +proto.minute = proto.minutes = getSetMinute; + +// Second +proto.second = proto.seconds = getSetSecond; + +// Millisecond +proto.millisecond = proto.milliseconds = getSetMillisecond; + +// Offset +proto.utcOffset = getSetOffset; +proto.utc = setOffsetToUTC; +proto.local = setOffsetToLocal; +proto.parseZone = setOffsetToParsedOffset; +proto.hasAlignedHourOffset = hasAlignedHourOffset; +proto.isDST = isDaylightSavingTime; +proto.isLocal = isLocal; +proto.isUtcOffset = isUtcOffset; +proto.isUtc = isUtc; +proto.isUTC = isUtc; + +// Timezone +proto.zoneAbbr = getZoneAbbr; +proto.zoneName = getZoneName; + +// Deprecations +proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); +proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); +proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); +proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); +proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); + +function createUnix (input) { + return createLocal(input * 1000); +} + +function createInZone () { + return createLocal.apply(null, arguments).parseZone(); +} + +function preParsePostFormat (string) { + return string; +} + +var proto$1 = Locale.prototype; + +proto$1.calendar = calendar; +proto$1.longDateFormat = longDateFormat; +proto$1.invalidDate = invalidDate; +proto$1.ordinal = ordinal; +proto$1.preparse = preParsePostFormat; +proto$1.postformat = preParsePostFormat; +proto$1.relativeTime = relativeTime; +proto$1.pastFuture = pastFuture; +proto$1.set = set; + +// Month +proto$1.months = localeMonths; +proto$1.monthsShort = localeMonthsShort; +proto$1.monthsParse = localeMonthsParse; +proto$1.monthsRegex = monthsRegex; +proto$1.monthsShortRegex = monthsShortRegex; + +// Week +proto$1.week = localeWeek; +proto$1.firstDayOfYear = localeFirstDayOfYear; +proto$1.firstDayOfWeek = localeFirstDayOfWeek; + +// Day of Week +proto$1.weekdays = localeWeekdays; +proto$1.weekdaysMin = localeWeekdaysMin; +proto$1.weekdaysShort = localeWeekdaysShort; +proto$1.weekdaysParse = localeWeekdaysParse; + +proto$1.weekdaysRegex = weekdaysRegex; +proto$1.weekdaysShortRegex = weekdaysShortRegex; +proto$1.weekdaysMinRegex = weekdaysMinRegex; + +// Hours +proto$1.isPM = localeIsPM; +proto$1.meridiem = localeMeridiem; + +function get$1 (format, index, field, setter) { + var locale = getLocale(); + var utc = createUTC().set(setter, index); + return locale[field](utc, format); +} + +function listMonthsImpl (format, index, field) { + if (isNumber(format)) { + index = format; + format = undefined; } - var defaultInvalidDate = 'Invalid date'; + format = format || ''; - function invalidDate () { - return this._invalidDate; + if (index != null) { + return get$1(format, index, field, 'month'); } - var defaultOrdinal = '%d'; - var defaultOrdinalParse = /\d{1,2}/; - - function ordinal (number) { - return this._ordinal.replace('%d', number); + var i; + var out = []; + for (i = 0; i < 12; i++) { + out[i] = get$1(format, i, field, 'month'); } + return out; +} - function preParsePostFormat (string) { - return string; - } - - var defaultRelativeTime = { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }; - - function relative__relativeTime (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (isFunction(output)) ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - } - - function pastFuture (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return isFunction(format) ? format(output) : format.replace(/%s/i, output); - } - - var prototype__proto = Locale.prototype; - - prototype__proto._calendar = defaultCalendar; - prototype__proto.calendar = locale_calendar__calendar; - prototype__proto._longDateFormat = defaultLongDateFormat; - prototype__proto.longDateFormat = longDateFormat; - prototype__proto._invalidDate = defaultInvalidDate; - prototype__proto.invalidDate = invalidDate; - prototype__proto._ordinal = defaultOrdinal; - prototype__proto.ordinal = ordinal; - prototype__proto._ordinalParse = defaultOrdinalParse; - prototype__proto.preparse = preParsePostFormat; - prototype__proto.postformat = preParsePostFormat; - prototype__proto._relativeTime = defaultRelativeTime; - prototype__proto.relativeTime = relative__relativeTime; - prototype__proto.pastFuture = pastFuture; - prototype__proto.set = locale_set__set; - - // Month - prototype__proto.months = localeMonths; - prototype__proto._months = defaultLocaleMonths; - prototype__proto.monthsShort = localeMonthsShort; - prototype__proto._monthsShort = defaultLocaleMonthsShort; - prototype__proto.monthsParse = localeMonthsParse; - prototype__proto._monthsRegex = defaultMonthsRegex; - prototype__proto.monthsRegex = monthsRegex; - prototype__proto._monthsShortRegex = defaultMonthsShortRegex; - prototype__proto.monthsShortRegex = monthsShortRegex; - - // Week - prototype__proto.week = localeWeek; - prototype__proto._week = defaultLocaleWeek; - prototype__proto.firstDayOfYear = localeFirstDayOfYear; - prototype__proto.firstDayOfWeek = localeFirstDayOfWeek; - - // Day of Week - prototype__proto.weekdays = localeWeekdays; - prototype__proto._weekdays = defaultLocaleWeekdays; - prototype__proto.weekdaysMin = localeWeekdaysMin; - prototype__proto._weekdaysMin = defaultLocaleWeekdaysMin; - prototype__proto.weekdaysShort = localeWeekdaysShort; - prototype__proto._weekdaysShort = defaultLocaleWeekdaysShort; - prototype__proto.weekdaysParse = localeWeekdaysParse; - - prototype__proto._weekdaysRegex = defaultWeekdaysRegex; - prototype__proto.weekdaysRegex = weekdaysRegex; - prototype__proto._weekdaysShortRegex = defaultWeekdaysShortRegex; - prototype__proto.weekdaysShortRegex = weekdaysShortRegex; - prototype__proto._weekdaysMinRegex = defaultWeekdaysMinRegex; - prototype__proto.weekdaysMinRegex = weekdaysMinRegex; - - // Hours - prototype__proto.isPM = localeIsPM; - prototype__proto._meridiemParse = defaultLocaleMeridiemParse; - prototype__proto.meridiem = localeMeridiem; - - function lists__get (format, index, field, setter) { - var locale = locale_locales__getLocale(); - var utc = create_utc__createUTC().set(setter, index); - return locale[field](utc, format); - } - - function listMonthsImpl (format, index, field) { - if (typeof format === 'number') { +// () +// (5) +// (fmt, 5) +// (fmt) +// (true) +// (true, 5) +// (true, fmt, 5) +// (true, fmt) +function listWeekdaysImpl (localeSorted, format, index, field) { + if (typeof localeSorted === 'boolean') { + if (isNumber(format)) { index = format; format = undefined; } format = format || ''; + } else { + format = localeSorted; + index = format; + localeSorted = false; - if (index != null) { - return lists__get(format, index, field, 'month'); - } - - var i; - var out = []; - for (i = 0; i < 12; i++) { - out[i] = lists__get(format, i, field, 'month'); - } - return out; - } - - // () - // (5) - // (fmt, 5) - // (fmt) - // (true) - // (true, 5) - // (true, fmt, 5) - // (true, fmt) - function listWeekdaysImpl (localeSorted, format, index, field) { - if (typeof localeSorted === 'boolean') { - if (typeof format === 'number') { - index = format; - format = undefined; - } - - format = format || ''; - } else { - format = localeSorted; + if (isNumber(format)) { index = format; - localeSorted = false; - - if (typeof format === 'number') { - index = format; - format = undefined; - } - - format = format || ''; + format = undefined; } - var locale = locale_locales__getLocale(), - shift = localeSorted ? locale._week.dow : 0; - - if (index != null) { - return lists__get(format, (index + shift) % 7, field, 'day'); - } - - var i; - var out = []; - for (i = 0; i < 7; i++) { - out[i] = lists__get(format, (i + shift) % 7, field, 'day'); - } - return out; + format = format || ''; } - function lists__listMonths (format, index) { - return listMonthsImpl(format, index, 'months'); + var locale = getLocale(), + shift = localeSorted ? locale._week.dow : 0; + + if (index != null) { + return get$1(format, (index + shift) % 7, field, 'day'); } - function lists__listMonthsShort (format, index) { - return listMonthsImpl(format, index, 'monthsShort'); + var i; + var out = []; + for (i = 0; i < 7; i++) { + out[i] = get$1(format, (i + shift) % 7, field, 'day'); + } + return out; +} + +function listMonths (format, index) { + return listMonthsImpl(format, index, 'months'); +} + +function listMonthsShort (format, index) { + return listMonthsImpl(format, index, 'monthsShort'); +} + +function listWeekdays (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); +} + +function listWeekdaysShort (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); +} + +function listWeekdaysMin (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); +} + +getSetGlobalLocale('en', { + dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } +}); + +// Side effect imports +hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); +hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); + +var mathAbs = Math.abs; + +function abs () { + var data = this._data; + + this._milliseconds = mathAbs(this._milliseconds); + this._days = mathAbs(this._days); + this._months = mathAbs(this._months); + + data.milliseconds = mathAbs(data.milliseconds); + data.seconds = mathAbs(data.seconds); + data.minutes = mathAbs(data.minutes); + data.hours = mathAbs(data.hours); + data.months = mathAbs(data.months); + data.years = mathAbs(data.years); + + return this; +} + +function addSubtract$1 (duration, input, value, direction) { + var other = createDuration(input, value); + + duration._milliseconds += direction * other._milliseconds; + duration._days += direction * other._days; + duration._months += direction * other._months; + + return duration._bubble(); +} + +// supports only 2.0-style add(1, 's') or add(duration) +function add$1 (input, value) { + return addSubtract$1(this, input, value, 1); +} + +// supports only 2.0-style subtract(1, 's') or subtract(duration) +function subtract$1 (input, value) { + return addSubtract$1(this, input, value, -1); +} + +function absCeil (number) { + if (number < 0) { + return Math.floor(number); + } else { + return Math.ceil(number); + } +} + +function bubble () { + var milliseconds = this._milliseconds; + var days = this._days; + var months = this._months; + var data = this._data; + var seconds, minutes, hours, years, monthsFromDays; + + // if we have a mix of positive and negative values, bubble down first + // check: https://github.com/moment/moment/issues/2166 + if (!((milliseconds >= 0 && days >= 0 && months >= 0) || + (milliseconds <= 0 && days <= 0 && months <= 0))) { + milliseconds += absCeil(monthsToDays(months) + days) * 864e5; + days = 0; + months = 0; } - function lists__listWeekdays (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; + + seconds = absFloor(milliseconds / 1000); + data.seconds = seconds % 60; + + minutes = absFloor(seconds / 60); + data.minutes = minutes % 60; + + hours = absFloor(minutes / 60); + data.hours = hours % 24; + + days += absFloor(hours / 24); + + // convert days to months + monthsFromDays = absFloor(daysToMonths(days)); + months += monthsFromDays; + days -= absCeil(monthsToDays(monthsFromDays)); + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + data.days = days; + data.months = months; + data.years = years; + + return this; +} + +function daysToMonths (days) { + // 400 years have 146097 days (taking into account leap year rules) + // 400 years have 12 months === 4800 + return days * 4800 / 146097; +} + +function monthsToDays (months) { + // the reverse of daysToMonths + return months * 146097 / 4800; +} + +function as (units) { + if (!this.isValid()) { + return NaN; } + var days; + var months; + var milliseconds = this._milliseconds; - function lists__listWeekdaysShort (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); - } + units = normalizeUnits(units); - function lists__listWeekdaysMin (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); - } - - locale_locales__getSetGlobalLocale('en', { - ordinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); - - // Side effect imports - utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale); - utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale); - - var mathAbs = Math.abs; - - function duration_abs__abs () { - var data = this._data; - - this._milliseconds = mathAbs(this._milliseconds); - this._days = mathAbs(this._days); - this._months = mathAbs(this._months); - - data.milliseconds = mathAbs(data.milliseconds); - data.seconds = mathAbs(data.seconds); - data.minutes = mathAbs(data.minutes); - data.hours = mathAbs(data.hours); - data.months = mathAbs(data.months); - data.years = mathAbs(data.years); - - return this; - } - - function duration_add_subtract__addSubtract (duration, input, value, direction) { - var other = create__createDuration(input, value); - - duration._milliseconds += direction * other._milliseconds; - duration._days += direction * other._days; - duration._months += direction * other._months; - - return duration._bubble(); - } - - // supports only 2.0-style add(1, 's') or add(duration) - function duration_add_subtract__add (input, value) { - return duration_add_subtract__addSubtract(this, input, value, 1); - } - - // supports only 2.0-style subtract(1, 's') or subtract(duration) - function duration_add_subtract__subtract (input, value) { - return duration_add_subtract__addSubtract(this, input, value, -1); - } - - function absCeil (number) { - if (number < 0) { - return Math.floor(number); - } else { - return Math.ceil(number); + if (units === 'month' || units === 'year') { + days = this._days + milliseconds / 864e5; + months = this._months + daysToMonths(days); + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(monthsToDays(this._months)); + switch (units) { + case 'week' : return days / 7 + milliseconds / 6048e5; + case 'day' : return days + milliseconds / 864e5; + case 'hour' : return days * 24 + milliseconds / 36e5; + case 'minute' : return days * 1440 + milliseconds / 6e4; + case 'second' : return days * 86400 + milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 864e5) + milliseconds; + default: throw new Error('Unknown unit ' + units); } } +} - function bubble () { - var milliseconds = this._milliseconds; - var days = this._days; - var months = this._months; - var data = this._data; - var seconds, minutes, hours, years, monthsFromDays; - - // if we have a mix of positive and negative values, bubble down first - // check: https://github.com/moment/moment/issues/2166 - if (!((milliseconds >= 0 && days >= 0 && months >= 0) || - (milliseconds <= 0 && days <= 0 && months <= 0))) { - milliseconds += absCeil(monthsToDays(months) + days) * 864e5; - days = 0; - months = 0; - } - - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; - - seconds = absFloor(milliseconds / 1000); - data.seconds = seconds % 60; - - minutes = absFloor(seconds / 60); - data.minutes = minutes % 60; - - hours = absFloor(minutes / 60); - data.hours = hours % 24; - - days += absFloor(hours / 24); - - // convert days to months - monthsFromDays = absFloor(daysToMonths(days)); - months += monthsFromDays; - days -= absCeil(monthsToDays(monthsFromDays)); - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - data.days = days; - data.months = months; - data.years = years; - - return this; +// TODO: Use this.as('ms')? +function valueOf$1 () { + if (!this.isValid()) { + return NaN; } + return ( + this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6 + ); +} - function daysToMonths (days) { - // 400 years have 146097 days (taking into account leap year rules) - // 400 years have 12 months === 4800 - return days * 4800 / 146097; - } - - function monthsToDays (months) { - // the reverse of daysToMonths - return months * 146097 / 4800; - } - - function as (units) { - var days; - var months; - var milliseconds = this._milliseconds; - - units = normalizeUnits(units); - - if (units === 'month' || units === 'year') { - days = this._days + milliseconds / 864e5; - months = this._months + daysToMonths(days); - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(monthsToDays(this._months)); - switch (units) { - case 'week' : return days / 7 + milliseconds / 6048e5; - case 'day' : return days + milliseconds / 864e5; - case 'hour' : return days * 24 + milliseconds / 36e5; - case 'minute' : return days * 1440 + milliseconds / 6e4; - case 'second' : return days * 86400 + milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 864e5) + milliseconds; - default: throw new Error('Unknown unit ' + units); - } - } - } - - // TODO: Use this.as('ms')? - function duration_as__valueOf () { - return ( - this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6 - ); - } - - function makeAs (alias) { - return function () { - return this.as(alias); - }; - } - - var asMilliseconds = makeAs('ms'); - var asSeconds = makeAs('s'); - var asMinutes = makeAs('m'); - var asHours = makeAs('h'); - var asDays = makeAs('d'); - var asWeeks = makeAs('w'); - var asMonths = makeAs('M'); - var asYears = makeAs('y'); - - function duration_get__get (units) { - units = normalizeUnits(units); - return this[units + 's'](); - } - - function makeGetter(name) { - return function () { - return this._data[name]; - }; - } - - var milliseconds = makeGetter('milliseconds'); - var seconds = makeGetter('seconds'); - var minutes = makeGetter('minutes'); - var hours = makeGetter('hours'); - var days = makeGetter('days'); - var months = makeGetter('months'); - var years = makeGetter('years'); - - function weeks () { - return absFloor(this.days() / 7); - } - - var round = Math.round; - var thresholds = { - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month - M: 11 // months to year +function makeAs (alias) { + return function () { + return this.as(alias); }; +} - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); +var asMilliseconds = makeAs('ms'); +var asSeconds = makeAs('s'); +var asMinutes = makeAs('m'); +var asHours = makeAs('h'); +var asDays = makeAs('d'); +var asWeeks = makeAs('w'); +var asMonths = makeAs('M'); +var asYears = makeAs('y'); + +function get$2 (units) { + units = normalizeUnits(units); + return this.isValid() ? this[units + 's']() : NaN; +} + +function makeGetter(name) { + return function () { + return this.isValid() ? this._data[name] : NaN; + }; +} + +var milliseconds = makeGetter('milliseconds'); +var seconds = makeGetter('seconds'); +var minutes = makeGetter('minutes'); +var hours = makeGetter('hours'); +var days = makeGetter('days'); +var months = makeGetter('months'); +var years = makeGetter('years'); + +function weeks () { + return absFloor(this.days() / 7); +} + +var round = Math.round; +var thresholds = { + ss: 44, // a few seconds to seconds + s : 45, // seconds to minute + m : 45, // minutes to hour + h : 22, // hours to day + d : 26, // days to month + M : 11 // months to year +}; + +// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize +function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); +} + +function relativeTime$1 (posNegDuration, withoutSuffix, locale) { + var duration = createDuration(posNegDuration).abs(); + var seconds = round(duration.as('s')); + var minutes = round(duration.as('m')); + var hours = round(duration.as('h')); + var days = round(duration.as('d')); + var months = round(duration.as('M')); + var years = round(duration.as('y')); + + var a = seconds <= thresholds.ss && ['s', seconds] || + seconds < thresholds.s && ['ss', seconds] || + minutes <= 1 && ['m'] || + minutes < thresholds.m && ['mm', minutes] || + hours <= 1 && ['h'] || + hours < thresholds.h && ['hh', hours] || + days <= 1 && ['d'] || + days < thresholds.d && ['dd', days] || + months <= 1 && ['M'] || + months < thresholds.M && ['MM', months] || + years <= 1 && ['y'] || ['yy', years]; + + a[2] = withoutSuffix; + a[3] = +posNegDuration > 0; + a[4] = locale; + return substituteTimeAgo.apply(null, a); +} + +// This function allows you to set the rounding function for relative time strings +function getSetRelativeTimeRounding (roundingFunction) { + if (roundingFunction === undefined) { + return round; } - - function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) { - var duration = create__createDuration(posNegDuration).abs(); - var seconds = round(duration.as('s')); - var minutes = round(duration.as('m')); - var hours = round(duration.as('h')); - var days = round(duration.as('d')); - var months = round(duration.as('M')); - var years = round(duration.as('y')); - - var a = seconds < thresholds.s && ['s', seconds] || - minutes <= 1 && ['m'] || - minutes < thresholds.m && ['mm', minutes] || - hours <= 1 && ['h'] || - hours < thresholds.h && ['hh', hours] || - days <= 1 && ['d'] || - days < thresholds.d && ['dd', days] || - months <= 1 && ['M'] || - months < thresholds.M && ['MM', months] || - years <= 1 && ['y'] || ['yy', years]; - - a[2] = withoutSuffix; - a[3] = +posNegDuration > 0; - a[4] = locale; - return substituteTimeAgo.apply(null, a); - } - - // This function allows you to set a threshold for relative time strings - function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) { - if (thresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return thresholds[threshold]; - } - thresholds[threshold] = limit; + if (typeof(roundingFunction) === 'function') { + round = roundingFunction; return true; } + return false; +} - function humanize (withSuffix) { - var locale = this.localeData(); - var output = duration_humanize__relativeTime(this, !withSuffix, locale); +// This function allows you to set a threshold for relative time strings +function getSetRelativeTimeThreshold (threshold, limit) { + if (thresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return thresholds[threshold]; + } + thresholds[threshold] = limit; + if (threshold === 's') { + thresholds.ss = limit - 1; + } + return true; +} - if (withSuffix) { - output = locale.pastFuture(+this, output); - } - - return locale.postformat(output); +function humanize (withSuffix) { + if (!this.isValid()) { + return this.localeData().invalidDate(); } - var iso_string__abs = Math.abs; + var locale = this.localeData(); + var output = relativeTime$1(this, !withSuffix, locale); - function iso_string__toISOString() { - // for ISO strings we do not use the normal bubbling rules: - // * milliseconds bubble up until they become hours - // * days do not bubble at all - // * months bubble up until they become years - // This is because there is no context-free conversion between hours and days - // (think of clock changes) - // and also not between days and months (28-31 days per month) - var seconds = iso_string__abs(this._milliseconds) / 1000; - var days = iso_string__abs(this._days); - var months = iso_string__abs(this._months); - var minutes, hours, years; - - // 3600 seconds -> 60 minutes -> 1 hour - minutes = absFloor(seconds / 60); - hours = absFloor(minutes / 60); - seconds %= 60; - minutes %= 60; - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var Y = years; - var M = months; - var D = days; - var h = hours; - var m = minutes; - var s = seconds; - var total = this.asSeconds(); - - if (!total) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } - - return (total < 0 ? '-' : '') + - 'P' + - (Y ? Y + 'Y' : '') + - (M ? M + 'M' : '') + - (D ? D + 'D' : '') + - ((h || m || s) ? 'T' : '') + - (h ? h + 'H' : '') + - (m ? m + 'M' : '') + - (s ? s + 'S' : ''); + if (withSuffix) { + output = locale.pastFuture(+this, output); } - var duration_prototype__proto = Duration.prototype; + return locale.postformat(output); +} - duration_prototype__proto.abs = duration_abs__abs; - duration_prototype__proto.add = duration_add_subtract__add; - duration_prototype__proto.subtract = duration_add_subtract__subtract; - duration_prototype__proto.as = as; - duration_prototype__proto.asMilliseconds = asMilliseconds; - duration_prototype__proto.asSeconds = asSeconds; - duration_prototype__proto.asMinutes = asMinutes; - duration_prototype__proto.asHours = asHours; - duration_prototype__proto.asDays = asDays; - duration_prototype__proto.asWeeks = asWeeks; - duration_prototype__proto.asMonths = asMonths; - duration_prototype__proto.asYears = asYears; - duration_prototype__proto.valueOf = duration_as__valueOf; - duration_prototype__proto._bubble = bubble; - duration_prototype__proto.get = duration_get__get; - duration_prototype__proto.milliseconds = milliseconds; - duration_prototype__proto.seconds = seconds; - duration_prototype__proto.minutes = minutes; - duration_prototype__proto.hours = hours; - duration_prototype__proto.days = days; - duration_prototype__proto.weeks = weeks; - duration_prototype__proto.months = months; - duration_prototype__proto.years = years; - duration_prototype__proto.humanize = humanize; - duration_prototype__proto.toISOString = iso_string__toISOString; - duration_prototype__proto.toString = iso_string__toISOString; - duration_prototype__proto.toJSON = iso_string__toISOString; - duration_prototype__proto.locale = locale; - duration_prototype__proto.localeData = localeData; +var abs$1 = Math.abs; - // Deprecations - duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString); - duration_prototype__proto.lang = lang; +function toISOString$1() { + // for ISO strings we do not use the normal bubbling rules: + // * milliseconds bubble up until they become hours + // * days do not bubble at all + // * months bubble up until they become years + // This is because there is no context-free conversion between hours and days + // (think of clock changes) + // and also not between days and months (28-31 days per month) + if (!this.isValid()) { + return this.localeData().invalidDate(); + } - // Side effect imports + var seconds = abs$1(this._milliseconds) / 1000; + var days = abs$1(this._days); + var months = abs$1(this._months); + var minutes, hours, years; - // FORMATTING + // 3600 seconds -> 60 minutes -> 1 hour + minutes = absFloor(seconds / 60); + hours = absFloor(minutes / 60); + seconds %= 60; + minutes %= 60; - addFormatToken('X', 0, 0, 'unix'); - addFormatToken('x', 0, 0, 'valueOf'); - - // PARSING - - addRegexToken('x', matchSigned); - addRegexToken('X', matchTimestamp); - addParseToken('X', function (input, array, config) { - config._d = new Date(parseFloat(input, 10) * 1000); - }); - addParseToken('x', function (input, array, config) { - config._d = new Date(toInt(input)); - }); - - // Side effect imports + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; - utils_hooks__hooks.version = '2.13.0'; + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var Y = years; + var M = months; + var D = days; + var h = hours; + var m = minutes; + var s = seconds; + var total = this.asSeconds(); - setHookCallback(local__createLocal); + if (!total) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } - utils_hooks__hooks.fn = momentPrototype; - utils_hooks__hooks.min = min; - utils_hooks__hooks.max = max; - utils_hooks__hooks.now = now; - utils_hooks__hooks.utc = create_utc__createUTC; - utils_hooks__hooks.unix = moment__createUnix; - utils_hooks__hooks.months = lists__listMonths; - utils_hooks__hooks.isDate = isDate; - utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale; - utils_hooks__hooks.invalid = valid__createInvalid; - utils_hooks__hooks.duration = create__createDuration; - utils_hooks__hooks.isMoment = isMoment; - utils_hooks__hooks.weekdays = lists__listWeekdays; - utils_hooks__hooks.parseZone = moment__createInZone; - utils_hooks__hooks.localeData = locale_locales__getLocale; - utils_hooks__hooks.isDuration = isDuration; - utils_hooks__hooks.monthsShort = lists__listMonthsShort; - utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin; - utils_hooks__hooks.defineLocale = defineLocale; - utils_hooks__hooks.updateLocale = updateLocale; - utils_hooks__hooks.locales = locale_locales__listLocales; - utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort; - utils_hooks__hooks.normalizeUnits = normalizeUnits; - utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold; - utils_hooks__hooks.prototype = momentPrototype; + return (total < 0 ? '-' : '') + + 'P' + + (Y ? Y + 'Y' : '') + + (M ? M + 'M' : '') + + (D ? D + 'D' : '') + + ((h || m || s) ? 'T' : '') + + (h ? h + 'H' : '') + + (m ? m + 'M' : '') + + (s ? s + 'S' : ''); +} - var _moment = utils_hooks__hooks; +var proto$2 = Duration.prototype; - return _moment; +proto$2.isValid = isValid$1; +proto$2.abs = abs; +proto$2.add = add$1; +proto$2.subtract = subtract$1; +proto$2.as = as; +proto$2.asMilliseconds = asMilliseconds; +proto$2.asSeconds = asSeconds; +proto$2.asMinutes = asMinutes; +proto$2.asHours = asHours; +proto$2.asDays = asDays; +proto$2.asWeeks = asWeeks; +proto$2.asMonths = asMonths; +proto$2.asYears = asYears; +proto$2.valueOf = valueOf$1; +proto$2._bubble = bubble; +proto$2.get = get$2; +proto$2.milliseconds = milliseconds; +proto$2.seconds = seconds; +proto$2.minutes = minutes; +proto$2.hours = hours; +proto$2.days = days; +proto$2.weeks = weeks; +proto$2.months = months; +proto$2.years = years; +proto$2.humanize = humanize; +proto$2.toISOString = toISOString$1; +proto$2.toString = toISOString$1; +proto$2.toJSON = toISOString$1; +proto$2.locale = locale; +proto$2.localeData = localeData; -})); +// Deprecations +proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); +proto$2.lang = lang; + +// Side effect imports + +// FORMATTING + +addFormatToken('X', 0, 0, 'unix'); +addFormatToken('x', 0, 0, 'valueOf'); + +// PARSING + +addRegexToken('x', matchSigned); +addRegexToken('X', matchTimestamp); +addParseToken('X', function (input, array, config) { + config._d = new Date(parseFloat(input, 10) * 1000); +}); +addParseToken('x', function (input, array, config) { + config._d = new Date(toInt(input)); +}); + +// Side effect imports + + +hooks.version = '2.18.1'; + +setHookCallback(createLocal); + +hooks.fn = proto; +hooks.min = min; +hooks.max = max; +hooks.now = now; +hooks.utc = createUTC; +hooks.unix = createUnix; +hooks.months = listMonths; +hooks.isDate = isDate; +hooks.locale = getSetGlobalLocale; +hooks.invalid = createInvalid; +hooks.duration = createDuration; +hooks.isMoment = isMoment; +hooks.weekdays = listWeekdays; +hooks.parseZone = createInZone; +hooks.localeData = getLocale; +hooks.isDuration = isDuration; +hooks.monthsShort = listMonthsShort; +hooks.weekdaysMin = listWeekdaysMin; +hooks.defineLocale = defineLocale; +hooks.updateLocale = updateLocale; +hooks.locales = listLocales; +hooks.weekdaysShort = listWeekdaysShort; +hooks.normalizeUnits = normalizeUnits; +hooks.relativeTimeRounding = getSetRelativeTimeRounding; +hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; +hooks.calendarFormat = getCalendarFormat; +hooks.prototype = proto; + +return hooks; + +}))); diff --git a/lib/javascripts/moment_locale/af.js b/lib/javascripts/moment_locale/af.js old mode 100755 new mode 100644 index 1a96bf4735..a89257fdc4 --- a/lib/javascripts/moment_locale/af.js +++ b/lib/javascripts/moment_locale/af.js @@ -1,73 +1,73 @@ //! moment.js locale configuration -//! locale : afrikaans (af) +//! locale : Afrikaans [af] //! author : Werner Mollentze : https://github.com/wernerm ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var af = moment.defineLocale('af', { - months : 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des'.split('_'), - weekdays : 'Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag'.split('_'), - weekdaysShort : 'Son_Maa_Din_Woe_Don_Vry_Sat'.split('_'), - weekdaysMin : 'So_Ma_Di_Wo_Do_Vr_Sa'.split('_'), - meridiemParse: /vm|nm/i, - isPM : function (input) { - return /^nm$/i.test(input); - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 12) { - return isLower ? 'vm' : 'VM'; - } else { - return isLower ? 'nm' : 'NM'; - } - }, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Vandag om] LT', - nextDay : '[Môre om] LT', - nextWeek : 'dddd [om] LT', - lastDay : '[Gister om] LT', - lastWeek : '[Laas] dddd [om] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'oor %s', - past : '%s gelede', - s : '\'n paar sekondes', - m : '\'n minuut', - mm : '%d minute', - h : '\'n uur', - hh : '%d ure', - d : '\'n dag', - dd : '%d dae', - M : '\'n maand', - MM : '%d maande', - y : '\'n jaar', - yy : '%d jaar' - }, - ordinalParse: /\d{1,2}(ste|de)/, - ordinal : function (number) { - return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); // Thanks to Joris Röling : https://github.com/jjupiter - }, - week : { - dow : 1, // Maandag is die eerste dag van die week. - doy : 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar. +var af = moment.defineLocale('af', { + months : 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split('_'), + monthsShort : 'Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des'.split('_'), + weekdays : 'Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag'.split('_'), + weekdaysShort : 'Son_Maa_Din_Woe_Don_Vry_Sat'.split('_'), + weekdaysMin : 'So_Ma_Di_Wo_Do_Vr_Sa'.split('_'), + meridiemParse: /vm|nm/i, + isPM : function (input) { + return /^nm$/i.test(input); + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 12) { + return isLower ? 'vm' : 'VM'; + } else { + return isLower ? 'nm' : 'NM'; } - }); + }, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Vandag om] LT', + nextDay : '[Môre om] LT', + nextWeek : 'dddd [om] LT', + lastDay : '[Gister om] LT', + lastWeek : '[Laas] dddd [om] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'oor %s', + past : '%s gelede', + s : '\'n paar sekondes', + m : '\'n minuut', + mm : '%d minute', + h : '\'n uur', + hh : '%d ure', + d : '\'n dag', + dd : '%d dae', + M : '\'n maand', + MM : '%d maande', + y : '\'n jaar', + yy : '%d jaar' + }, + dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, + ordinal : function (number) { + return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); // Thanks to Joris Röling : https://github.com/jjupiter + }, + week : { + dow : 1, // Maandag is die eerste dag van die week. + doy : 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar. + } +}); - return af; +return af; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ar-dz.js b/lib/javascripts/moment_locale/ar-dz.js new file mode 100644 index 0000000000..f394594463 --- /dev/null +++ b/lib/javascripts/moment_locale/ar-dz.js @@ -0,0 +1,59 @@ +//! moment.js locale configuration +//! locale : Arabic (Algeria) [ar-dz] +//! author : Noureddine LOUAHEDJ : https://github.com/noureddineme + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var arDz = moment.defineLocale('ar-dz', { + months : 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), + monthsShort : 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), + weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), + weekdaysShort : 'احد_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'), + weekdaysMin : 'أح_إث_ثلا_أر_خم_جم_سب'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[اليوم على الساعة] LT', + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'في %s', + past : 'منذ %s', + s : 'ثوان', + m : 'دقيقة', + mm : '%d دقائق', + h : 'ساعة', + hh : '%d ساعات', + d : 'يوم', + dd : '%d أيام', + M : 'شهر', + MM : '%d أشهر', + y : 'سنة', + yy : '%d سنوات' + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 4 // The week that contains Jan 1st is the first week of the year. + } +}); + +return arDz; + +}))); diff --git a/lib/javascripts/moment_locale/ar-kw.js b/lib/javascripts/moment_locale/ar-kw.js new file mode 100644 index 0000000000..93c7dd54b4 --- /dev/null +++ b/lib/javascripts/moment_locale/ar-kw.js @@ -0,0 +1,59 @@ +//! moment.js locale configuration +//! locale : Arabic (Kuwait) [ar-kw] +//! author : Nusret Parlak: https://github.com/nusretparlak + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var arKw = moment.defineLocale('ar-kw', { + months : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), + monthsShort : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), + weekdays : 'الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), + weekdaysShort : 'احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'), + weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[اليوم على الساعة] LT', + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'في %s', + past : 'منذ %s', + s : 'ثوان', + m : 'دقيقة', + mm : '%d دقائق', + h : 'ساعة', + hh : '%d ساعات', + d : 'يوم', + dd : '%d أيام', + M : 'شهر', + MM : '%d أشهر', + y : 'سنة', + yy : '%d سنوات' + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); + +return arKw; + +}))); diff --git a/lib/javascripts/moment_locale/ar-ly.js b/lib/javascripts/moment_locale/ar-ly.js new file mode 100644 index 0000000000..7180ed4ed8 --- /dev/null +++ b/lib/javascripts/moment_locale/ar-ly.js @@ -0,0 +1,126 @@ +//! moment.js locale configuration +//! locale : Arabic (Lybia) [ar-ly] +//! author : Ali Hmer: https://github.com/kikoanis + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var symbolMap = { + '1': '1', + '2': '2', + '3': '3', + '4': '4', + '5': '5', + '6': '6', + '7': '7', + '8': '8', + '9': '9', + '0': '0' +}; +var pluralForm = function (n) { + return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5; +}; +var plurals = { + s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'], + m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'], + h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'], + d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'], + M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'], + y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام'] +}; +var pluralize = function (u) { + return function (number, withoutSuffix, string, isFuture) { + var f = pluralForm(number), + str = plurals[u][pluralForm(number)]; + if (f === 2) { + str = str[withoutSuffix ? 0 : 1]; + } + return str.replace(/%d/i, number); + }; +}; +var months = [ + 'يناير', + 'فبراير', + 'مارس', + 'أبريل', + 'مايو', + 'يونيو', + 'يوليو', + 'أغسطس', + 'سبتمبر', + 'أكتوبر', + 'نوفمبر', + 'ديسمبر' +]; + +var arLy = moment.defineLocale('ar-ly', { + months : months, + monthsShort : months, + weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), + weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), + weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'D/\u200FM/\u200FYYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + meridiemParse: /ص|م/, + isPM : function (input) { + return 'م' === input; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'ص'; + } else { + return 'م'; + } + }, + calendar : { + sameDay: '[اليوم عند الساعة] LT', + nextDay: '[غدًا عند الساعة] LT', + nextWeek: 'dddd [عند الساعة] LT', + lastDay: '[أمس عند الساعة] LT', + lastWeek: 'dddd [عند الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'بعد %s', + past : 'منذ %s', + s : pluralize('s'), + m : pluralize('m'), + mm : pluralize('m'), + h : pluralize('h'), + hh : pluralize('h'), + d : pluralize('d'), + dd : pluralize('d'), + M : pluralize('M'), + MM : pluralize('M'), + y : pluralize('y'), + yy : pluralize('y') + }, + preparse: function (string) { + return string.replace(/\u200f/g, '').replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }).replace(/,/g, '،'); + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); + +return arLy; + +}))); diff --git a/lib/javascripts/moment_locale/ar-ma.js b/lib/javascripts/moment_locale/ar-ma.js old mode 100755 new mode 100644 index 923a4930e2..cbd810ba79 --- a/lib/javascripts/moment_locale/ar-ma.js +++ b/lib/javascripts/moment_locale/ar-ma.js @@ -1,60 +1,60 @@ //! moment.js locale configuration -//! locale : Moroccan Arabic (ar-ma) +//! locale : Arabic (Morocco) [ar-ma] //! author : ElFadili Yassine : https://github.com/ElFadiliY //! author : Abdel Said : https://github.com/abdelsaid ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ar_ma = moment.defineLocale('ar-ma', { - months : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), - monthsShort : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), - weekdays : 'الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'في %s', - past : 'منذ %s', - s : 'ثوان', - m : 'دقيقة', - mm : '%d دقائق', - h : 'ساعة', - hh : '%d ساعات', - d : 'يوم', - dd : '%d أيام', - M : 'شهر', - MM : '%d أشهر', - y : 'سنة', - yy : '%d سنوات' - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); +var arMa = moment.defineLocale('ar-ma', { + months : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), + monthsShort : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), + weekdays : 'الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), + weekdaysShort : 'احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'), + weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[اليوم على الساعة] LT', + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'في %s', + past : 'منذ %s', + s : 'ثوان', + m : 'دقيقة', + mm : '%d دقائق', + h : 'ساعة', + hh : '%d ساعات', + d : 'يوم', + dd : '%d أيام', + M : 'شهر', + MM : '%d أشهر', + y : 'سنة', + yy : '%d سنوات' + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); - return ar_ma; +return arMa; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ar-sa.js b/lib/javascripts/moment_locale/ar-sa.js old mode 100755 new mode 100644 index aecb80a51b..dccd0d8330 --- a/lib/javascripts/moment_locale/ar-sa.js +++ b/lib/javascripts/moment_locale/ar-sa.js @@ -1,104 +1,105 @@ //! moment.js locale configuration -//! locale : Arabic Saudi Arabia (ar-sa) +//! locale : Arabic (Saudi Arabia) [ar-sa] //! author : Suhail Alkowaileet : https://github.com/xsoh ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '١', - '2': '٢', - '3': '٣', - '4': '٤', - '5': '٥', - '6': '٦', - '7': '٧', - '8': '٨', - '9': '٩', - '0': '٠' - }, numberMap = { - '١': '1', - '٢': '2', - '٣': '3', - '٤': '4', - '٥': '5', - '٦': '6', - '٧': '7', - '٨': '8', - '٩': '9', - '٠': '0' - }; +var symbolMap = { + '1': '١', + '2': '٢', + '3': '٣', + '4': '٤', + '5': '٥', + '6': '٦', + '7': '٧', + '8': '٨', + '9': '٩', + '0': '٠' +}; +var numberMap = { + '١': '1', + '٢': '2', + '٣': '3', + '٤': '4', + '٥': '5', + '٦': '6', + '٧': '7', + '٨': '8', + '٩': '9', + '٠': '0' +}; - var ar_sa = moment.defineLocale('ar-sa', { - months : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - monthsShort : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - meridiemParse: /ص|م/, - isPM : function (input) { - return 'م' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ص'; - } else { - return 'م'; - } - }, - calendar : { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'في %s', - past : 'منذ %s', - s : 'ثوان', - m : 'دقيقة', - mm : '%d دقائق', - h : 'ساعة', - hh : '%d ساعات', - d : 'يوم', - dd : '%d أيام', - M : 'شهر', - MM : '%d أشهر', - y : 'سنة', - yy : '%d سنوات' - }, - preparse: function (string) { - return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { - return numberMap[match]; - }).replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }).replace(/,/g, '،'); - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. +var arSa = moment.defineLocale('ar-sa', { + months : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), + monthsShort : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), + weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), + weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), + weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + meridiemParse: /ص|م/, + isPM : function (input) { + return 'م' === input; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'ص'; + } else { + return 'م'; } - }); + }, + calendar : { + sameDay: '[اليوم على الساعة] LT', + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'في %s', + past : 'منذ %s', + s : 'ثوان', + m : 'دقيقة', + mm : '%d دقائق', + h : 'ساعة', + hh : '%d ساعات', + d : 'يوم', + dd : '%d أيام', + M : 'شهر', + MM : '%d أشهر', + y : 'سنة', + yy : '%d سنوات' + }, + preparse: function (string) { + return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { + return numberMap[match]; + }).replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }).replace(/,/g, '،'); + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); - return ar_sa; +return arSa; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ar-tn.js b/lib/javascripts/moment_locale/ar-tn.js old mode 100755 new mode 100644 index b8e07a0bc5..5f0d38bb06 --- a/lib/javascripts/moment_locale/ar-tn.js +++ b/lib/javascripts/moment_locale/ar-tn.js @@ -1,58 +1,59 @@ //! moment.js locale configuration -//! locale : Tunisian Arabic (ar-tn) +//! locale : Arabic (Tunisia) [ar-tn] +//! author : Nader Toukabri : https://github.com/naderio ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ar_tn = moment.defineLocale('ar-tn', { - months: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - monthsShort: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd D MMMM YYYY HH:mm' - }, - calendar: { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime: { - future: 'في %s', - past: 'منذ %s', - s: 'ثوان', - m: 'دقيقة', - mm: '%d دقائق', - h: 'ساعة', - hh: '%d ساعات', - d: 'يوم', - dd: '%d أيام', - M: 'شهر', - MM: '%d أشهر', - y: 'سنة', - yy: '%d سنوات' - }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var arTn = moment.defineLocale('ar-tn', { + months: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), + monthsShort: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), + weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), + weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), + weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'), + weekdaysParseExact : true, + longDateFormat: { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L: 'DD/MM/YYYY', + LL: 'D MMMM YYYY', + LLL: 'D MMMM YYYY HH:mm', + LLLL: 'dddd D MMMM YYYY HH:mm' + }, + calendar: { + sameDay: '[اليوم على الساعة] LT', + nextDay: '[غدا على الساعة] LT', + nextWeek: 'dddd [على الساعة] LT', + lastDay: '[أمس على الساعة] LT', + lastWeek: 'dddd [على الساعة] LT', + sameElse: 'L' + }, + relativeTime: { + future: 'في %s', + past: 'منذ %s', + s: 'ثوان', + m: 'دقيقة', + mm: '%d دقائق', + h: 'ساعة', + hh: '%d ساعات', + d: 'يوم', + dd: '%d أيام', + M: 'شهر', + MM: '%d أشهر', + y: 'سنة', + yy: '%d سنوات' + }, + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return ar_tn; +return arTn; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ar.js b/lib/javascripts/moment_locale/ar.js old mode 100755 new mode 100644 index b478c0f588..d263e4c4f8 --- a/lib/javascripts/moment_locale/ar.js +++ b/lib/javascripts/moment_locale/ar.js @@ -1,137 +1,142 @@ //! moment.js locale configuration -//! Locale: Arabic (ar) -//! Author: Abdel Said: https://github.com/abdelsaid -//! Changes in months, weekdays: Ahmed Elkhatib -//! Native plural forms: forabi https://github.com/forabi +//! locale : Arabic [ar] +//! author : Abdel Said: https://github.com/abdelsaid +//! author : Ahmed Elkhatib +//! author : forabi https://github.com/forabi ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '١', - '2': '٢', - '3': '٣', - '4': '٤', - '5': '٥', - '6': '٦', - '7': '٧', - '8': '٨', - '9': '٩', - '0': '٠' - }, numberMap = { - '١': '1', - '٢': '2', - '٣': '3', - '٤': '4', - '٥': '5', - '٦': '6', - '٧': '7', - '٨': '8', - '٩': '9', - '٠': '0' - }, pluralForm = function (n) { - return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5; - }, plurals = { - s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'], - m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'], - h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'], - d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'], - M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'], - y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام'] - }, pluralize = function (u) { - return function (number, withoutSuffix, string, isFuture) { - var f = pluralForm(number), - str = plurals[u][pluralForm(number)]; - if (f === 2) { - str = str[withoutSuffix ? 0 : 1]; - } - return str.replace(/%d/i, number); - }; - }, months = [ - 'كانون الثاني يناير', - 'شباط فبراير', - 'آذار مارس', - 'نيسان أبريل', - 'أيار مايو', - 'حزيران يونيو', - 'تموز يوليو', - 'آب أغسطس', - 'أيلول سبتمبر', - 'تشرين الأول أكتوبر', - 'تشرين الثاني نوفمبر', - 'كانون الأول ديسمبر' - ]; - - var ar = moment.defineLocale('ar', { - months : months, - monthsShort : months, - weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'D/\u200FM/\u200FYYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - meridiemParse: /ص|م/, - isPM : function (input) { - return 'م' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ص'; - } else { - return 'م'; - } - }, - calendar : { - sameDay: '[اليوم عند الساعة] LT', - nextDay: '[غدًا عند الساعة] LT', - nextWeek: 'dddd [عند الساعة] LT', - lastDay: '[أمس عند الساعة] LT', - lastWeek: 'dddd [عند الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'بعد %s', - past : 'منذ %s', - s : pluralize('s'), - m : pluralize('m'), - mm : pluralize('m'), - h : pluralize('h'), - hh : pluralize('h'), - d : pluralize('d'), - dd : pluralize('d'), - M : pluralize('M'), - MM : pluralize('M'), - y : pluralize('y'), - yy : pluralize('y') - }, - preparse: function (string) { - return string.replace(/\u200f/g, '').replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { - return numberMap[match]; - }).replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }).replace(/,/g, '،'); - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. +var symbolMap = { + '1': '١', + '2': '٢', + '3': '٣', + '4': '٤', + '5': '٥', + '6': '٦', + '7': '٧', + '8': '٨', + '9': '٩', + '0': '٠' +}; +var numberMap = { + '١': '1', + '٢': '2', + '٣': '3', + '٤': '4', + '٥': '5', + '٦': '6', + '٧': '7', + '٨': '8', + '٩': '9', + '٠': '0' +}; +var pluralForm = function (n) { + return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5; +}; +var plurals = { + s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'], + m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'], + h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'], + d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'], + M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'], + y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام'] +}; +var pluralize = function (u) { + return function (number, withoutSuffix, string, isFuture) { + var f = pluralForm(number), + str = plurals[u][pluralForm(number)]; + if (f === 2) { + str = str[withoutSuffix ? 0 : 1]; } - }); + return str.replace(/%d/i, number); + }; +}; +var months = [ + 'كانون الثاني يناير', + 'شباط فبراير', + 'آذار مارس', + 'نيسان أبريل', + 'أيار مايو', + 'حزيران يونيو', + 'تموز يوليو', + 'آب أغسطس', + 'أيلول سبتمبر', + 'تشرين الأول أكتوبر', + 'تشرين الثاني نوفمبر', + 'كانون الأول ديسمبر' +]; - return ar; +var ar = moment.defineLocale('ar', { + months : months, + monthsShort : months, + weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), + weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), + weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'D/\u200FM/\u200FYYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + meridiemParse: /ص|م/, + isPM : function (input) { + return 'م' === input; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'ص'; + } else { + return 'م'; + } + }, + calendar : { + sameDay: '[اليوم عند الساعة] LT', + nextDay: '[غدًا عند الساعة] LT', + nextWeek: 'dddd [عند الساعة] LT', + lastDay: '[أمس عند الساعة] LT', + lastWeek: 'dddd [عند الساعة] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'بعد %s', + past : 'منذ %s', + s : pluralize('s'), + m : pluralize('m'), + mm : pluralize('m'), + h : pluralize('h'), + hh : pluralize('h'), + d : pluralize('d'), + dd : pluralize('d'), + M : pluralize('M'), + MM : pluralize('M'), + y : pluralize('y'), + yy : pluralize('y') + }, + preparse: function (string) { + return string.replace(/\u200f/g, '').replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { + return numberMap[match]; + }).replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }).replace(/,/g, '،'); + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); -})); \ No newline at end of file +return ar; + +}))); diff --git a/lib/javascripts/moment_locale/az.js b/lib/javascripts/moment_locale/az.js old mode 100755 new mode 100644 index 1beeca5c91..56021b4463 --- a/lib/javascripts/moment_locale/az.js +++ b/lib/javascripts/moment_locale/az.js @@ -1,105 +1,105 @@ //! moment.js locale configuration -//! locale : azerbaijani (az) +//! locale : Azerbaijani [az] //! author : topchiyev : https://github.com/topchiyev ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var suffixes = { - 1: '-inci', - 5: '-inci', - 8: '-inci', - 70: '-inci', - 80: '-inci', - 2: '-nci', - 7: '-nci', - 20: '-nci', - 50: '-nci', - 3: '-üncü', - 4: '-üncü', - 100: '-üncü', - 6: '-ncı', - 9: '-uncu', - 10: '-uncu', - 30: '-uncu', - 60: '-ıncı', - 90: '-ıncı' - }; +var suffixes = { + 1: '-inci', + 5: '-inci', + 8: '-inci', + 70: '-inci', + 80: '-inci', + 2: '-nci', + 7: '-nci', + 20: '-nci', + 50: '-nci', + 3: '-üncü', + 4: '-üncü', + 100: '-üncü', + 6: '-ncı', + 9: '-uncu', + 10: '-uncu', + 30: '-uncu', + 60: '-ıncı', + 90: '-ıncı' +}; - var az = moment.defineLocale('az', { - months : 'yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr'.split('_'), - monthsShort : 'yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek'.split('_'), - weekdays : 'Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə'.split('_'), - weekdaysShort : 'Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən'.split('_'), - weekdaysMin : 'Bz_BE_ÇA_Çə_CA_Cü_Şə'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[bugün saat] LT', - nextDay : '[sabah saat] LT', - nextWeek : '[gələn həftə] dddd [saat] LT', - lastDay : '[dünən] LT', - lastWeek : '[keçən həftə] dddd [saat] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s sonra', - past : '%s əvvəl', - s : 'birneçə saniyyə', - m : 'bir dəqiqə', - mm : '%d dəqiqə', - h : 'bir saat', - hh : '%d saat', - d : 'bir gün', - dd : '%d gün', - M : 'bir ay', - MM : '%d ay', - y : 'bir il', - yy : '%d il' - }, - meridiemParse: /gecə|səhər|gündüz|axşam/, - isPM : function (input) { - return /^(gündüz|axşam)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'gecə'; - } else if (hour < 12) { - return 'səhər'; - } else if (hour < 17) { - return 'gündüz'; - } else { - return 'axşam'; - } - }, - ordinalParse: /\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/, - ordinal : function (number) { - if (number === 0) { // special case for zero - return number + '-ıncı'; - } - var a = number % 10, - b = number % 100 - a, - c = number >= 100 ? 100 : null; - return number + (suffixes[a] || suffixes[b] || suffixes[c]); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var az = moment.defineLocale('az', { + months : 'yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr'.split('_'), + monthsShort : 'yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek'.split('_'), + weekdays : 'Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə'.split('_'), + weekdaysShort : 'Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən'.split('_'), + weekdaysMin : 'Bz_BE_ÇA_Çə_CA_Cü_Şə'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[bugün saat] LT', + nextDay : '[sabah saat] LT', + nextWeek : '[gələn həftə] dddd [saat] LT', + lastDay : '[dünən] LT', + lastWeek : '[keçən həftə] dddd [saat] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s sonra', + past : '%s əvvəl', + s : 'birneçə saniyyə', + m : 'bir dəqiqə', + mm : '%d dəqiqə', + h : 'bir saat', + hh : '%d saat', + d : 'bir gün', + dd : '%d gün', + M : 'bir ay', + MM : '%d ay', + y : 'bir il', + yy : '%d il' + }, + meridiemParse: /gecə|səhər|gündüz|axşam/, + isPM : function (input) { + return /^(gündüz|axşam)$/.test(input); + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'gecə'; + } else if (hour < 12) { + return 'səhər'; + } else if (hour < 17) { + return 'gündüz'; + } else { + return 'axşam'; } - }); + }, + dayOfMonthOrdinalParse: /\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/, + ordinal : function (number) { + if (number === 0) { // special case for zero + return number + '-ıncı'; + } + var a = number % 10, + b = number % 100 - a, + c = number >= 100 ? 100 : null; + return number + (suffixes[a] || suffixes[b] || suffixes[c]); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return az; +return az; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/be.js b/lib/javascripts/moment_locale/be.js old mode 100755 new mode 100644 index c6294b34f4..83025fdc44 --- a/lib/javascripts/moment_locale/be.js +++ b/lib/javascripts/moment_locale/be.js @@ -1,5 +1,5 @@ //! moment.js locale configuration -//! locale : belarusian (be) +//! locale : Belarusian [be] //! author : Dmitry Demidov : https://github.com/demidov91 //! author: Praleska: http://praleska.pro/ //! Author : Menelion Elensúle : https://github.com/Oire @@ -7,64 +7,64 @@ ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function plural(word, num) { - var forms = word.split('_'); - return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +function plural(word, num) { + var forms = word.split('_'); + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +} +function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': withoutSuffix ? 'хвіліна_хвіліны_хвілін' : 'хвіліну_хвіліны_хвілін', + 'hh': withoutSuffix ? 'гадзіна_гадзіны_гадзін' : 'гадзіну_гадзіны_гадзін', + 'dd': 'дзень_дні_дзён', + 'MM': 'месяц_месяцы_месяцаў', + 'yy': 'год_гады_гадоў' + }; + if (key === 'm') { + return withoutSuffix ? 'хвіліна' : 'хвіліну'; } - function relativeTimeWithPlural(number, withoutSuffix, key) { - var format = { - 'mm': withoutSuffix ? 'хвіліна_хвіліны_хвілін' : 'хвіліну_хвіліны_хвілін', - 'hh': withoutSuffix ? 'гадзіна_гадзіны_гадзін' : 'гадзіну_гадзіны_гадзін', - 'dd': 'дзень_дні_дзён', - 'MM': 'месяц_месяцы_месяцаў', - 'yy': 'год_гады_гадоў' - }; - if (key === 'm') { - return withoutSuffix ? 'хвіліна' : 'хвіліну'; - } - else if (key === 'h') { - return withoutSuffix ? 'гадзіна' : 'гадзіну'; - } - else { - return number + ' ' + plural(format[key], +number); - } + else if (key === 'h') { + return withoutSuffix ? 'гадзіна' : 'гадзіну'; } + else { + return number + ' ' + plural(format[key], +number); + } +} - var be = moment.defineLocale('be', { - months : { - format: 'студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня'.split('_'), - standalone: 'студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань'.split('_') +var be = moment.defineLocale('be', { + months : { + format: 'студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня'.split('_'), + standalone: 'студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань'.split('_') + }, + monthsShort : 'студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж'.split('_'), + weekdays : { + format: 'нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу'.split('_'), + standalone: 'нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота'.split('_'), + isFormat: /\[ ?[Вв] ?(?:мінулую|наступную)? ?\] ?dddd/ + }, + weekdaysShort : 'нд_пн_ат_ср_чц_пт_сб'.split('_'), + weekdaysMin : 'нд_пн_ат_ср_чц_пт_сб'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY г.', + LLL : 'D MMMM YYYY г., HH:mm', + LLLL : 'dddd, D MMMM YYYY г., HH:mm' + }, + calendar : { + sameDay: '[Сёння ў] LT', + nextDay: '[Заўтра ў] LT', + lastDay: '[Учора ў] LT', + nextWeek: function () { + return '[У] dddd [ў] LT'; }, - monthsShort : 'студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж'.split('_'), - weekdays : { - format: 'нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу'.split('_'), - standalone: 'нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота'.split('_'), - isFormat: /\[ ?[Вв] ?(?:мінулую|наступную)? ?\] ?dddd/ - }, - weekdaysShort : 'нд_пн_ат_ср_чц_пт_сб'.split('_'), - weekdaysMin : 'нд_пн_ат_ср_чц_пт_сб'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY г.', - LLL : 'D MMMM YYYY г., HH:mm', - LLLL : 'dddd, D MMMM YYYY г., HH:mm' - }, - calendar : { - sameDay: '[Сёння ў] LT', - nextDay: '[Заўтра ў] LT', - lastDay: '[Учора ў] LT', - nextWeek: function () { - return '[У] dddd [ў] LT'; - }, - lastWeek: function () { - switch (this.day()) { + lastWeek: function () { + switch (this.day()) { case 0: case 3: case 5: @@ -74,43 +74,43 @@ case 2: case 4: return '[У мінулы] dddd [ў] LT'; - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'праз %s', - past : '%s таму', - s : 'некалькі секунд', - m : relativeTimeWithPlural, - mm : relativeTimeWithPlural, - h : relativeTimeWithPlural, - hh : relativeTimeWithPlural, - d : 'дзень', - dd : relativeTimeWithPlural, - M : 'месяц', - MM : relativeTimeWithPlural, - y : 'год', - yy : relativeTimeWithPlural - }, - meridiemParse: /ночы|раніцы|дня|вечара/, - isPM : function (input) { - return /^(дня|вечара)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ночы'; - } else if (hour < 12) { - return 'раніцы'; - } else if (hour < 17) { - return 'дня'; - } else { - return 'вечара'; } }, - ordinalParse: /\d{1,2}-(і|ы|га)/, - ordinal: function (number, period) { - switch (period) { + sameElse: 'L' + }, + relativeTime : { + future : 'праз %s', + past : '%s таму', + s : 'некалькі секунд', + m : relativeTimeWithPlural, + mm : relativeTimeWithPlural, + h : relativeTimeWithPlural, + hh : relativeTimeWithPlural, + d : 'дзень', + dd : relativeTimeWithPlural, + M : 'месяц', + MM : relativeTimeWithPlural, + y : 'год', + yy : relativeTimeWithPlural + }, + meridiemParse: /ночы|раніцы|дня|вечара/, + isPM : function (input) { + return /^(дня|вечара)$/.test(input); + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'ночы'; + } else if (hour < 12) { + return 'раніцы'; + } else if (hour < 17) { + return 'дня'; + } else { + return 'вечара'; + } + }, + dayOfMonthOrdinalParse: /\d{1,2}-(і|ы|га)/, + ordinal: function (number, period) { + switch (period) { case 'M': case 'd': case 'DDD': @@ -121,14 +121,14 @@ return number + '-га'; default: return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return be; +return be; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/bg.js b/lib/javascripts/moment_locale/bg.js old mode 100755 new mode 100644 index 169e1238a1..ee06d1978a --- a/lib/javascripts/moment_locale/bg.js +++ b/lib/javascripts/moment_locale/bg.js @@ -1,36 +1,36 @@ //! moment.js locale configuration -//! locale : bulgarian (bg) +//! locale : Bulgarian [bg] //! author : Krasen Borisov : https://github.com/kraz ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var bg = moment.defineLocale('bg', { - months : 'януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември'.split('_'), - monthsShort : 'янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек'.split('_'), - weekdays : 'неделя_понеделник_вторник_сряда_четвъртък_петък_събота'.split('_'), - weekdaysShort : 'нед_пон_вто_сря_чет_пет_съб'.split('_'), - weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'D.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd, D MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[Днес в] LT', - nextDay : '[Утре в] LT', - nextWeek : 'dddd [в] LT', - lastDay : '[Вчера в] LT', - lastWeek : function () { - switch (this.day()) { +var bg = moment.defineLocale('bg', { + months : 'януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември'.split('_'), + monthsShort : 'янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек'.split('_'), + weekdays : 'неделя_понеделник_вторник_сряда_четвъртък_петък_събота'.split('_'), + weekdaysShort : 'нед_пон_вто_сря_чет_пет_съб'.split('_'), + weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'D.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY H:mm', + LLLL : 'dddd, D MMMM YYYY H:mm' + }, + calendar : { + sameDay : '[Днес в] LT', + nextDay : '[Утре в] LT', + nextWeek : 'dddd [в] LT', + lastDay : '[Вчера в] LT', + lastWeek : function () { + switch (this.day()) { case 0: case 3: case 6: @@ -40,51 +40,51 @@ case 4: case 5: return '[В изминалия] dddd [в] LT'; - } - }, - sameElse : 'L' - }, - relativeTime : { - future : 'след %s', - past : 'преди %s', - s : 'няколко секунди', - m : 'минута', - mm : '%d минути', - h : 'час', - hh : '%d часа', - d : 'ден', - dd : '%d дни', - M : 'месец', - MM : '%d месеца', - y : 'година', - yy : '%d години' - }, - ordinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/, - ordinal : function (number) { - var lastDigit = number % 10, - last2Digits = number % 100; - if (number === 0) { - return number + '-ев'; - } else if (last2Digits === 0) { - return number + '-ен'; - } else if (last2Digits > 10 && last2Digits < 20) { - return number + '-ти'; - } else if (lastDigit === 1) { - return number + '-ви'; - } else if (lastDigit === 2) { - return number + '-ри'; - } else if (lastDigit === 7 || lastDigit === 8) { - return number + '-ми'; - } else { - return number + '-ти'; } }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. + sameElse : 'L' + }, + relativeTime : { + future : 'след %s', + past : 'преди %s', + s : 'няколко секунди', + m : 'минута', + mm : '%d минути', + h : 'час', + hh : '%d часа', + d : 'ден', + dd : '%d дни', + M : 'месец', + MM : '%d месеца', + y : 'година', + yy : '%d години' + }, + dayOfMonthOrdinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/, + ordinal : function (number) { + var lastDigit = number % 10, + last2Digits = number % 100; + if (number === 0) { + return number + '-ев'; + } else if (last2Digits === 0) { + return number + '-ен'; + } else if (last2Digits > 10 && last2Digits < 20) { + return number + '-ти'; + } else if (lastDigit === 1) { + return number + '-ви'; + } else if (lastDigit === 2) { + return number + '-ри'; + } else if (lastDigit === 7 || lastDigit === 8) { + return number + '-ми'; + } else { + return number + '-ти'; } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return bg; +return bg; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/bn.js b/lib/javascripts/moment_locale/bn.js old mode 100755 new mode 100644 index f6ae2603eb..b6f942a6dc --- a/lib/javascripts/moment_locale/bn.js +++ b/lib/javascripts/moment_locale/bn.js @@ -1,119 +1,119 @@ //! moment.js locale configuration -//! locale : Bengali (bn) +//! locale : Bengali [bn] //! author : Kaushik Gandhi : https://github.com/kaushikgandhi ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '১', - '2': '২', - '3': '৩', - '4': '৪', - '5': '৫', - '6': '৬', - '7': '৭', - '8': '৮', - '9': '৯', - '0': '০' +var symbolMap = { + '1': '১', + '2': '২', + '3': '৩', + '4': '৪', + '5': '৫', + '6': '৬', + '7': '৭', + '8': '৮', + '9': '৯', + '0': '০' +}; +var numberMap = { + '১': '1', + '২': '2', + '৩': '3', + '৪': '4', + '৫': '5', + '৬': '6', + '৭': '7', + '৮': '8', + '৯': '9', + '০': '0' +}; + +var bn = moment.defineLocale('bn', { + months : 'জানুয়ারী_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split('_'), + monthsShort : 'জানু_ফেব_মার্চ_এপ্র_মে_জুন_জুল_আগ_সেপ্ট_অক্টো_নভে_ডিসে'.split('_'), + weekdays : 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার'.split('_'), + weekdaysShort : 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি'.split('_'), + weekdaysMin : 'রবি_সোম_মঙ্গ_বুধ_বৃহঃ_শুক্র_শনি'.split('_'), + longDateFormat : { + LT : 'A h:mm সময়', + LTS : 'A h:mm:ss সময়', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm সময়', + LLLL : 'dddd, D MMMM YYYY, A h:mm সময়' }, - numberMap = { - '১': '1', - '২': '2', - '৩': '3', - '৪': '4', - '৫': '5', - '৬': '6', - '৭': '7', - '৮': '8', - '৯': '9', - '০': '0' - }; - - var bn = moment.defineLocale('bn', { - months : 'জানুয়ারী_ফেবুয়ারী_মার্চ_এপ্রিল_মে_জুন_জুলাই_অগাস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split('_'), - monthsShort : 'জানু_ফেব_মার্চ_এপর_মে_জুন_জুল_অগ_সেপ্ট_অক্টো_নভ_ডিসেম্'.split('_'), - weekdays : 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পত্তিবার_শুক্রবার_শনিবার'.split('_'), - weekdaysShort : 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পত্তি_শুক্র_শনি'.split('_'), - weekdaysMin : 'রব_সম_মঙ্গ_বু_ব্রিহ_শু_শনি'.split('_'), - longDateFormat : { - LT : 'A h:mm সময়', - LTS : 'A h:mm:ss সময়', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm সময়', - LLLL : 'dddd, D MMMM YYYY, A h:mm সময়' - }, - calendar : { - sameDay : '[আজ] LT', - nextDay : '[আগামীকাল] LT', - nextWeek : 'dddd, LT', - lastDay : '[গতকাল] LT', - lastWeek : '[গত] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s পরে', - past : '%s আগে', - s : 'কয়েক সেকেন্ড', - m : 'এক মিনিট', - mm : '%d মিনিট', - h : 'এক ঘন্টা', - hh : '%d ঘন্টা', - d : 'এক দিন', - dd : '%d দিন', - M : 'এক মাস', - MM : '%d মাস', - y : 'এক বছর', - yy : '%d বছর' - }, - preparse: function (string) { - return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) { - return numberMap[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }); - }, - meridiemParse: /রাত|সকাল|দুপুর|বিকাল|রাত/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if ((meridiem === 'রাত' && hour >= 4) || - (meridiem === 'দুপুর' && hour < 5) || - meridiem === 'বিকাল') { - return hour + 12; - } else { - return hour; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'রাত'; - } else if (hour < 10) { - return 'সকাল'; - } else if (hour < 17) { - return 'দুপুর'; - } else if (hour < 20) { - return 'বিকাল'; - } else { - return 'রাত'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. + calendar : { + sameDay : '[আজ] LT', + nextDay : '[আগামীকাল] LT', + nextWeek : 'dddd, LT', + lastDay : '[গতকাল] LT', + lastWeek : '[গত] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s পরে', + past : '%s আগে', + s : 'কয়েক সেকেন্ড', + m : 'এক মিনিট', + mm : '%d মিনিট', + h : 'এক ঘন্টা', + hh : '%d ঘন্টা', + d : 'এক দিন', + dd : '%d দিন', + M : 'এক মাস', + MM : '%d মাস', + y : 'এক বছর', + yy : '%d বছর' + }, + preparse: function (string) { + return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + meridiemParse: /রাত|সকাল|দুপুর|বিকাল|রাত/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if ((meridiem === 'রাত' && hour >= 4) || + (meridiem === 'দুপুর' && hour < 5) || + meridiem === 'বিকাল') { + return hour + 12; + } else { + return hour; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'রাত'; + } else if (hour < 10) { + return 'সকাল'; + } else if (hour < 17) { + return 'দুপুর'; + } else if (hour < 20) { + return 'বিকাল'; + } else { + return 'রাত'; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); - return bn; +return bn; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/bo.js b/lib/javascripts/moment_locale/bo.js old mode 100755 new mode 100644 index 77ea1bd41e..eb6db476d8 --- a/lib/javascripts/moment_locale/bo.js +++ b/lib/javascripts/moment_locale/bo.js @@ -1,119 +1,119 @@ //! moment.js locale configuration -//! locale : tibetan (bo) +//! locale : Tibetan [bo] //! author : Thupten N. Chakrishar : https://github.com/vajradog ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '༡', - '2': '༢', - '3': '༣', - '4': '༤', - '5': '༥', - '6': '༦', - '7': '༧', - '8': '༨', - '9': '༩', - '0': '༠' +var symbolMap = { + '1': '༡', + '2': '༢', + '3': '༣', + '4': '༤', + '5': '༥', + '6': '༦', + '7': '༧', + '8': '༨', + '9': '༩', + '0': '༠' +}; +var numberMap = { + '༡': '1', + '༢': '2', + '༣': '3', + '༤': '4', + '༥': '5', + '༦': '6', + '༧': '7', + '༨': '8', + '༩': '9', + '༠': '0' +}; + +var bo = moment.defineLocale('bo', { + months : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'), + monthsShort : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'), + weekdays : 'གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་'.split('_'), + weekdaysShort : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'), + weekdaysMin : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'), + longDateFormat : { + LT : 'A h:mm', + LTS : 'A h:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm', + LLLL : 'dddd, D MMMM YYYY, A h:mm' }, - numberMap = { - '༡': '1', - '༢': '2', - '༣': '3', - '༤': '4', - '༥': '5', - '༦': '6', - '༧': '7', - '༨': '8', - '༩': '9', - '༠': '0' - }; - - var bo = moment.defineLocale('bo', { - months : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'), - monthsShort : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'), - weekdays : 'གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་'.split('_'), - weekdaysShort : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'), - weekdaysMin : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'), - longDateFormat : { - LT : 'A h:mm', - LTS : 'A h:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm', - LLLL : 'dddd, D MMMM YYYY, A h:mm' - }, - calendar : { - sameDay : '[དི་རིང] LT', - nextDay : '[སང་ཉིན] LT', - nextWeek : '[བདུན་ཕྲག་རྗེས་མ], LT', - lastDay : '[ཁ་སང] LT', - lastWeek : '[བདུན་ཕྲག་མཐའ་མ] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s ལ་', - past : '%s སྔན་ལ', - s : 'ལམ་སང', - m : 'སྐར་མ་གཅིག', - mm : '%d སྐར་མ', - h : 'ཆུ་ཚོད་གཅིག', - hh : '%d ཆུ་ཚོད', - d : 'ཉིན་གཅིག', - dd : '%d ཉིན་', - M : 'ཟླ་བ་གཅིག', - MM : '%d ཟླ་བ', - y : 'ལོ་གཅིག', - yy : '%d ལོ' - }, - preparse: function (string) { - return string.replace(/[༡༢༣༤༥༦༧༨༩༠]/g, function (match) { - return numberMap[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }); - }, - meridiemParse: /མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if ((meridiem === 'མཚན་མོ' && hour >= 4) || - (meridiem === 'ཉིན་གུང' && hour < 5) || - meridiem === 'དགོང་དག') { - return hour + 12; - } else { - return hour; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'མཚན་མོ'; - } else if (hour < 10) { - return 'ཞོགས་ཀས'; - } else if (hour < 17) { - return 'ཉིན་གུང'; - } else if (hour < 20) { - return 'དགོང་དག'; - } else { - return 'མཚན་མོ'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. + calendar : { + sameDay : '[དི་རིང] LT', + nextDay : '[སང་ཉིན] LT', + nextWeek : '[བདུན་ཕྲག་རྗེས་མ], LT', + lastDay : '[ཁ་སང] LT', + lastWeek : '[བདུན་ཕྲག་མཐའ་མ] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s ལ་', + past : '%s སྔན་ལ', + s : 'ལམ་སང', + m : 'སྐར་མ་གཅིག', + mm : '%d སྐར་མ', + h : 'ཆུ་ཚོད་གཅིག', + hh : '%d ཆུ་ཚོད', + d : 'ཉིན་གཅིག', + dd : '%d ཉིན་', + M : 'ཟླ་བ་གཅིག', + MM : '%d ཟླ་བ', + y : 'ལོ་གཅིག', + yy : '%d ལོ' + }, + preparse: function (string) { + return string.replace(/[༡༢༣༤༥༦༧༨༩༠]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + meridiemParse: /མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if ((meridiem === 'མཚན་མོ' && hour >= 4) || + (meridiem === 'ཉིན་གུང' && hour < 5) || + meridiem === 'དགོང་དག') { + return hour + 12; + } else { + return hour; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'མཚན་མོ'; + } else if (hour < 10) { + return 'ཞོགས་ཀས'; + } else if (hour < 17) { + return 'ཉིན་གུང'; + } else if (hour < 20) { + return 'དགོང་དག'; + } else { + return 'མཚན་མོ'; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); - return bo; +return bo; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/br.js b/lib/javascripts/moment_locale/br.js old mode 100755 new mode 100644 index 7b42d428c0..723306315c --- a/lib/javascripts/moment_locale/br.js +++ b/lib/javascripts/moment_locale/br.js @@ -1,25 +1,25 @@ //! moment.js locale configuration -//! locale : breton (br) +//! locale : Breton [br] //! author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function relativeTimeWithMutation(number, withoutSuffix, key) { - var format = { - 'mm': 'munutenn', - 'MM': 'miz', - 'dd': 'devezh' - }; - return number + ' ' + mutation(format[key], number); - } - function specialMutationForYears(number) { - switch (lastNumber(number)) { +function relativeTimeWithMutation(number, withoutSuffix, key) { + var format = { + 'mm': 'munutenn', + 'MM': 'miz', + 'dd': 'devezh' + }; + return number + ' ' + mutation(format[key], number); +} +function specialMutationForYears(number) { + switch (lastNumber(number)) { case 1: case 3: case 4: @@ -28,81 +28,81 @@ return number + ' bloaz'; default: return number + ' vloaz'; - } } - function lastNumber(number) { - if (number > 9) { - return lastNumber(number % 10); - } - return number; +} +function lastNumber(number) { + if (number > 9) { + return lastNumber(number % 10); } - function mutation(text, number) { - if (number === 2) { - return softMutation(text); - } + return number; +} +function mutation(text, number) { + if (number === 2) { + return softMutation(text); + } + return text; +} +function softMutation(text) { + var mutationTable = { + 'm': 'v', + 'b': 'v', + 'd': 'z' + }; + if (mutationTable[text.charAt(0)] === undefined) { return text; } - function softMutation(text) { - var mutationTable = { - 'm': 'v', - 'b': 'v', - 'd': 'z' - }; - if (mutationTable[text.charAt(0)] === undefined) { - return text; - } - return mutationTable[text.charAt(0)] + text.substring(1); + return mutationTable[text.charAt(0)] + text.substring(1); +} + +var br = moment.defineLocale('br', { + months : 'Genver_C\'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu'.split('_'), + monthsShort : 'Gen_C\'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker'.split('_'), + weekdays : 'Sul_Lun_Meurzh_Merc\'her_Yaou_Gwener_Sadorn'.split('_'), + weekdaysShort : 'Sul_Lun_Meu_Mer_Yao_Gwe_Sad'.split('_'), + weekdaysMin : 'Su_Lu_Me_Mer_Ya_Gw_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'h[e]mm A', + LTS : 'h[e]mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D [a viz] MMMM YYYY', + LLL : 'D [a viz] MMMM YYYY h[e]mm A', + LLLL : 'dddd, D [a viz] MMMM YYYY h[e]mm A' + }, + calendar : { + sameDay : '[Hiziv da] LT', + nextDay : '[Warc\'hoazh da] LT', + nextWeek : 'dddd [da] LT', + lastDay : '[Dec\'h da] LT', + lastWeek : 'dddd [paset da] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'a-benn %s', + past : '%s \'zo', + s : 'un nebeud segondennoù', + m : 'ur vunutenn', + mm : relativeTimeWithMutation, + h : 'un eur', + hh : '%d eur', + d : 'un devezh', + dd : relativeTimeWithMutation, + M : 'ur miz', + MM : relativeTimeWithMutation, + y : 'ur bloaz', + yy : specialMutationForYears + }, + dayOfMonthOrdinalParse: /\d{1,2}(añ|vet)/, + ordinal : function (number) { + var output = (number === 1) ? 'añ' : 'vet'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } +}); - var br = moment.defineLocale('br', { - months : 'Genver_C\'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu'.split('_'), - monthsShort : 'Gen_C\'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker'.split('_'), - weekdays : 'Sul_Lun_Meurzh_Merc\'her_Yaou_Gwener_Sadorn'.split('_'), - weekdaysShort : 'Sul_Lun_Meu_Mer_Yao_Gwe_Sad'.split('_'), - weekdaysMin : 'Su_Lu_Me_Mer_Ya_Gw_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'h[e]mm A', - LTS : 'h[e]mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D [a viz] MMMM YYYY', - LLL : 'D [a viz] MMMM YYYY h[e]mm A', - LLLL : 'dddd, D [a viz] MMMM YYYY h[e]mm A' - }, - calendar : { - sameDay : '[Hiziv da] LT', - nextDay : '[Warc\'hoazh da] LT', - nextWeek : 'dddd [da] LT', - lastDay : '[Dec\'h da] LT', - lastWeek : 'dddd [paset da] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'a-benn %s', - past : '%s \'zo', - s : 'un nebeud segondennoù', - m : 'ur vunutenn', - mm : relativeTimeWithMutation, - h : 'un eur', - hh : '%d eur', - d : 'un devezh', - dd : relativeTimeWithMutation, - M : 'ur miz', - MM : relativeTimeWithMutation, - y : 'ur bloaz', - yy : specialMutationForYears - }, - ordinalParse: /\d{1,2}(añ|vet)/, - ordinal : function (number) { - var output = (number === 1) ? 'añ' : 'vet'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +return br; - return br; - -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/bs.js b/lib/javascripts/moment_locale/bs.js old mode 100755 new mode 100644 index bbe54dcdfe..760a786703 --- a/lib/javascripts/moment_locale/bs.js +++ b/lib/javascripts/moment_locale/bs.js @@ -1,19 +1,19 @@ //! moment.js locale configuration -//! locale : bosnian (bs) +//! locale : Bosnian [bs] //! author : Nedim Cholich : https://github.com/frontyard //! based on (hr) translation by Bojan Marković ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function translate(number, withoutSuffix, key) { - var result = number + ' '; - switch (key) { +function translate(number, withoutSuffix, key) { + var result = number + ' '; + switch (key) { case 'm': return withoutSuffix ? 'jedna minuta' : 'jedne minute'; case 'mm': @@ -61,30 +61,30 @@ result += 'godina'; } return result; - } } +} - var bs = moment.defineLocale('bs', { - months : 'januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar'.split('_'), - monthsShort : 'jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), - weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), - weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD. MM. YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[danas u] LT', - nextDay : '[sutra u] LT', - nextWeek : function () { - switch (this.day()) { +var bs = moment.defineLocale('bs', { + months : 'januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar'.split('_'), + monthsShort : 'jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.'.split('_'), + monthsParseExact: true, + weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), + weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), + weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY H:mm', + LLLL : 'dddd, D. MMMM YYYY H:mm' + }, + calendar : { + sameDay : '[danas u] LT', + nextDay : '[sutra u] LT', + nextWeek : function () { + switch (this.day()) { case 0: return '[u] [nedjelju] [u] LT'; case 3: @@ -96,11 +96,11 @@ case 4: case 5: return '[u] dddd [u] LT'; - } - }, - lastDay : '[jučer u] LT', - lastWeek : function () { - switch (this.day()) { + } + }, + lastDay : '[jučer u] LT', + lastWeek : function () { + switch (this.day()) { case 0: case 3: return '[prošlu] dddd [u] LT'; @@ -111,33 +111,33 @@ case 4: case 5: return '[prošli] dddd [u] LT'; - } - }, - sameElse : 'L' + } }, - relativeTime : { - future : 'za %s', - past : 'prije %s', - s : 'par sekundi', - m : translate, - mm : translate, - h : translate, - hh : translate, - d : 'dan', - dd : translate, - M : 'mjesec', - MM : translate, - y : 'godinu', - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + sameElse : 'L' + }, + relativeTime : { + future : 'za %s', + past : 'prije %s', + s : 'par sekundi', + m : translate, + mm : translate, + h : translate, + hh : translate, + d : 'dan', + dd : translate, + M : 'mjesec', + MM : translate, + y : 'godinu', + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return bs; +return bs; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ca.js b/lib/javascripts/moment_locale/ca.js old mode 100755 new mode 100644 index d796dc340a..bc490a240d --- a/lib/javascripts/moment_locale/ca.js +++ b/lib/javascripts/moment_locale/ca.js @@ -1,81 +1,88 @@ //! moment.js locale configuration -//! locale : catalan (ca) +//! locale : Catalan [ca] //! author : Juan G. Hurtado : https://github.com/juanghurtado ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ca = moment.defineLocale('ca', { - months : 'gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre'.split('_'), - monthsShort : 'gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.'.split('_'), - monthsParseExact : true, - weekdays : 'diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte'.split('_'), - weekdaysShort : 'dg._dl._dt._dc._dj._dv._ds.'.split('_'), - weekdaysMin : 'Dg_Dl_Dt_Dc_Dj_Dv_Ds'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd D MMMM YYYY H:mm' +var ca = moment.defineLocale('ca', { + months : { + standalone: 'gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre'.split('_'), + format: 'de gener_de febrer_de març_d\'abril_de maig_de juny_de juliol_d\'agost_de setembre_d\'octubre_de novembre_de desembre'.split('_'), + isFormat: /D[oD]?(\s)+MMMM/ + }, + monthsShort : 'gen._febr._març_abr._maig_juny_jul._ag._set._oct._nov._des.'.split('_'), + monthsParseExact : true, + weekdays : 'diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte'.split('_'), + weekdaysShort : 'dg._dl._dt._dc._dj._dv._ds.'.split('_'), + weekdaysMin : 'Dg_Dl_Dt_Dc_Dj_Dv_Ds'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD/MM/YYYY', + LL : '[el] D MMMM [de] YYYY', + ll : 'D MMM YYYY', + LLL : '[el] D MMMM [de] YYYY [a les] H:mm', + lll : 'D MMM YYYY, H:mm', + LLLL : '[el] dddd D MMMM [de] YYYY [a les] H:mm', + llll : 'ddd D MMM YYYY, H:mm' + }, + calendar : { + sameDay : function () { + return '[avui a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; }, - calendar : { - sameDay : function () { - return '[avui a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - nextDay : function () { - return '[demà a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - nextWeek : function () { - return 'dddd [a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - lastDay : function () { - return '[ahir a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - lastWeek : function () { - return '[el] dddd [passat a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - sameElse : 'L' + nextDay : function () { + return '[demà a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; }, - relativeTime : { - future : 'en %s', - past : 'fa %s', - s : 'uns segons', - m : 'un minut', - mm : '%d minuts', - h : 'una hora', - hh : '%d hores', - d : 'un dia', - dd : '%d dies', - M : 'un mes', - MM : '%d mesos', - y : 'un any', - yy : '%d anys' + nextWeek : function () { + return 'dddd [a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; }, - ordinalParse: /\d{1,2}(r|n|t|è|a)/, - ordinal : function (number, period) { - var output = (number === 1) ? 'r' : - (number === 2) ? 'n' : - (number === 3) ? 'r' : - (number === 4) ? 't' : 'è'; - if (period === 'w' || period === 'W') { - output = 'a'; - } - return number + output; + lastDay : function () { + return '[ahir a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. + lastWeek : function () { + return '[el] dddd [passat a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : 'd\'aquí %s', + past : 'fa %s', + s : 'uns segons', + m : 'un minut', + mm : '%d minuts', + h : 'una hora', + hh : '%d hores', + d : 'un dia', + dd : '%d dies', + M : 'un mes', + MM : '%d mesos', + y : 'un any', + yy : '%d anys' + }, + dayOfMonthOrdinalParse: /\d{1,2}(r|n|t|è|a)/, + ordinal : function (number, period) { + var output = (number === 1) ? 'r' : + (number === 2) ? 'n' : + (number === 3) ? 'r' : + (number === 4) ? 't' : 'è'; + if (period === 'w' || period === 'W') { + output = 'a'; } - }); + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return ca; +return ca; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/cs.js b/lib/javascripts/moment_locale/cs.js old mode 100755 new mode 100644 index 5854f70e2e..952dc49ed5 --- a/lib/javascripts/moment_locale/cs.js +++ b/lib/javascripts/moment_locale/cs.js @@ -1,23 +1,23 @@ //! moment.js locale configuration -//! locale : czech (cs) +//! locale : Czech [cs] //! author : petrbela : https://github.com/petrbela ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var months = 'leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec'.split('_'), - monthsShort = 'led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro'.split('_'); - function plural(n) { - return (n > 1) && (n < 5) && (~~(n / 10) !== 1); - } - function translate(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { +var months = 'leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec'.split('_'); +var monthsShort = 'led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro'.split('_'); +function plural(n) { + return (n > 1) && (n < 5) && (~~(n / 10) !== 1); +} +function translate(number, withoutSuffix, key, isFuture) { + var result = number + ' '; + switch (key) { case 's': // a few seconds / in a few seconds / a few seconds ago return (withoutSuffix || isFuture) ? 'pár sekund' : 'pár sekundami'; case 'm': // a minute / in a minute / a minute ago @@ -65,50 +65,51 @@ return result + 'lety'; } break; - } } +} - var cs = moment.defineLocale('cs', { - months : months, - monthsShort : monthsShort, - monthsParse : (function (months, monthsShort) { - var i, _monthsParse = []; - for (i = 0; i < 12; i++) { - // use custom parser to solve problem with July (červenec) - _monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i'); - } - return _monthsParse; - }(months, monthsShort)), - shortMonthsParse : (function (monthsShort) { - var i, _shortMonthsParse = []; - for (i = 0; i < 12; i++) { - _shortMonthsParse[i] = new RegExp('^' + monthsShort[i] + '$', 'i'); - } - return _shortMonthsParse; - }(monthsShort)), - longMonthsParse : (function (months) { - var i, _longMonthsParse = []; - for (i = 0; i < 12; i++) { - _longMonthsParse[i] = new RegExp('^' + months[i] + '$', 'i'); - } - return _longMonthsParse; - }(months)), - weekdays : 'neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota'.split('_'), - weekdaysShort : 'ne_po_út_st_čt_pá_so'.split('_'), - weekdaysMin : 'ne_po_út_st_čt_pá_so'.split('_'), - longDateFormat : { - LT: 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd D. MMMM YYYY H:mm' - }, - calendar : { - sameDay: '[dnes v] LT', - nextDay: '[zítra v] LT', - nextWeek: function () { - switch (this.day()) { +var cs = moment.defineLocale('cs', { + months : months, + monthsShort : monthsShort, + monthsParse : (function (months, monthsShort) { + var i, _monthsParse = []; + for (i = 0; i < 12; i++) { + // use custom parser to solve problem with July (červenec) + _monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i'); + } + return _monthsParse; + }(months, monthsShort)), + shortMonthsParse : (function (monthsShort) { + var i, _shortMonthsParse = []; + for (i = 0; i < 12; i++) { + _shortMonthsParse[i] = new RegExp('^' + monthsShort[i] + '$', 'i'); + } + return _shortMonthsParse; + }(monthsShort)), + longMonthsParse : (function (months) { + var i, _longMonthsParse = []; + for (i = 0; i < 12; i++) { + _longMonthsParse[i] = new RegExp('^' + months[i] + '$', 'i'); + } + return _longMonthsParse; + }(months)), + weekdays : 'neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota'.split('_'), + weekdaysShort : 'ne_po_út_st_čt_pá_so'.split('_'), + weekdaysMin : 'ne_po_út_st_čt_pá_so'.split('_'), + longDateFormat : { + LT: 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY H:mm', + LLLL : 'dddd D. MMMM YYYY H:mm', + l : 'D. M. YYYY' + }, + calendar : { + sameDay: '[dnes v] LT', + nextDay: '[zítra v] LT', + nextWeek: function () { + switch (this.day()) { case 0: return '[v neděli v] LT'; case 1: @@ -122,11 +123,11 @@ return '[v pátek v] LT'; case 6: return '[v sobotu v] LT'; - } - }, - lastDay: '[včera v] LT', - lastWeek: function () { - switch (this.day()) { + } + }, + lastDay: '[včera v] LT', + lastWeek: function () { + switch (this.day()) { case 0: return '[minulou neděli v] LT'; case 1: @@ -139,33 +140,33 @@ return '[minulý] dddd [v] LT'; case 6: return '[minulou sobotu v] LT'; - } - }, - sameElse: 'L' + } }, - relativeTime : { - future : 'za %s', - past : 'před %s', - s : translate, - m : translate, - mm : translate, - h : translate, - hh : translate, - d : translate, - dd : translate, - M : translate, - MM : translate, - y : translate, - yy : translate - }, - ordinalParse : /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); + sameElse: 'L' + }, + relativeTime : { + future : 'za %s', + past : 'před %s', + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + dayOfMonthOrdinalParse : /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return cs; +return cs; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/cv.js b/lib/javascripts/moment_locale/cv.js old mode 100755 new mode 100644 index a1d87e1846..6d20779840 --- a/lib/javascripts/moment_locale/cv.js +++ b/lib/javascripts/moment_locale/cv.js @@ -1,63 +1,63 @@ //! moment.js locale configuration -//! locale : chuvash (cv) +//! locale : Chuvash [cv] //! author : Anatoly Mironov : https://github.com/mirontoli ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var cv = moment.defineLocale('cv', { - months : 'кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав'.split('_'), - monthsShort : 'кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш'.split('_'), - weekdays : 'вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун'.split('_'), - weekdaysShort : 'выр_тун_ытл_юн_кӗҫ_эрн_шӑм'.split('_'), - weekdaysMin : 'вр_тн_ыт_юн_кҫ_эр_шм'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]', - LLL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm', - LLLL : 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm' +var cv = moment.defineLocale('cv', { + months : 'кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав'.split('_'), + monthsShort : 'кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш'.split('_'), + weekdays : 'вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун'.split('_'), + weekdaysShort : 'выр_тун_ытл_юн_кӗҫ_эрн_шӑм'.split('_'), + weekdaysMin : 'вр_тн_ыт_юн_кҫ_эр_шм'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD-MM-YYYY', + LL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]', + LLL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm', + LLLL : 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm' + }, + calendar : { + sameDay: '[Паян] LT [сехетре]', + nextDay: '[Ыран] LT [сехетре]', + lastDay: '[Ӗнер] LT [сехетре]', + nextWeek: '[Ҫитес] dddd LT [сехетре]', + lastWeek: '[Иртнӗ] dddd LT [сехетре]', + sameElse: 'L' + }, + relativeTime : { + future : function (output) { + var affix = /сехет$/i.exec(output) ? 'рен' : /ҫул$/i.exec(output) ? 'тан' : 'ран'; + return output + affix; }, - calendar : { - sameDay: '[Паян] LT [сехетре]', - nextDay: '[Ыран] LT [сехетре]', - lastDay: '[Ӗнер] LT [сехетре]', - nextWeek: '[Ҫитес] dddd LT [сехетре]', - lastWeek: '[Иртнӗ] dddd LT [сехетре]', - sameElse: 'L' - }, - relativeTime : { - future : function (output) { - var affix = /сехет$/i.exec(output) ? 'рен' : /ҫул$/i.exec(output) ? 'тан' : 'ран'; - return output + affix; - }, - past : '%s каялла', - s : 'пӗр-ик ҫеккунт', - m : 'пӗр минут', - mm : '%d минут', - h : 'пӗр сехет', - hh : '%d сехет', - d : 'пӗр кун', - dd : '%d кун', - M : 'пӗр уйӑх', - MM : '%d уйӑх', - y : 'пӗр ҫул', - yy : '%d ҫул' - }, - ordinalParse: /\d{1,2}-мӗш/, - ordinal : '%d-мӗш', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + past : '%s каялла', + s : 'пӗр-ик ҫеккунт', + m : 'пӗр минут', + mm : '%d минут', + h : 'пӗр сехет', + hh : '%d сехет', + d : 'пӗр кун', + dd : '%d кун', + M : 'пӗр уйӑх', + MM : '%d уйӑх', + y : 'пӗр ҫул', + yy : '%d ҫул' + }, + dayOfMonthOrdinalParse: /\d{1,2}-мӗш/, + ordinal : '%d-мӗш', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return cv; +return cv; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/cy.js b/lib/javascripts/moment_locale/cy.js old mode 100755 new mode 100644 index 958871e527..c570c22760 --- a/lib/javascripts/moment_locale/cy.js +++ b/lib/javascripts/moment_locale/cy.js @@ -1,80 +1,81 @@ //! moment.js locale configuration -//! locale : Welsh (cy) -//! author : Robert Allen +//! locale : Welsh [cy] +//! author : Robert Allen : https://github.com/robgallen +//! author : https://github.com/ryangreaves ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var cy = moment.defineLocale('cy', { - months: 'Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr'.split('_'), - monthsShort: 'Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag'.split('_'), - weekdays: 'Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn'.split('_'), - weekdaysShort: 'Sul_Llun_Maw_Mer_Iau_Gwe_Sad'.split('_'), - weekdaysMin: 'Su_Ll_Ma_Me_Ia_Gw_Sa'.split('_'), - weekdaysParseExact : true, - // time formats are the same as en-gb - longDateFormat: { - LT: 'HH:mm', - LTS : 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd, D MMMM YYYY HH:mm' - }, - calendar: { - sameDay: '[Heddiw am] LT', - nextDay: '[Yfory am] LT', - nextWeek: 'dddd [am] LT', - lastDay: '[Ddoe am] LT', - lastWeek: 'dddd [diwethaf am] LT', - sameElse: 'L' - }, - relativeTime: { - future: 'mewn %s', - past: '%s yn ôl', - s: 'ychydig eiliadau', - m: 'munud', - mm: '%d munud', - h: 'awr', - hh: '%d awr', - d: 'diwrnod', - dd: '%d diwrnod', - M: 'mis', - MM: '%d mis', - y: 'blwyddyn', - yy: '%d flynedd' - }, - ordinalParse: /\d{1,2}(fed|ain|af|il|ydd|ed|eg)/, - // traditional ordinal numbers above 31 are not commonly used in colloquial Welsh - ordinal: function (number) { - var b = number, - output = '', - lookup = [ - '', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed - 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed' // 11eg to 20fed - ]; - if (b > 20) { - if (b === 40 || b === 50 || b === 60 || b === 80 || b === 100) { - output = 'fed'; // not 30ain, 70ain or 90ain - } else { - output = 'ain'; - } - } else if (b > 0) { - output = lookup[b]; +var cy = moment.defineLocale('cy', { + months: 'Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr'.split('_'), + monthsShort: 'Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag'.split('_'), + weekdays: 'Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn'.split('_'), + weekdaysShort: 'Sul_Llun_Maw_Mer_Iau_Gwe_Sad'.split('_'), + weekdaysMin: 'Su_Ll_Ma_Me_Ia_Gw_Sa'.split('_'), + weekdaysParseExact : true, + // time formats are the same as en-gb + longDateFormat: { + LT: 'HH:mm', + LTS : 'HH:mm:ss', + L: 'DD/MM/YYYY', + LL: 'D MMMM YYYY', + LLL: 'D MMMM YYYY HH:mm', + LLLL: 'dddd, D MMMM YYYY HH:mm' + }, + calendar: { + sameDay: '[Heddiw am] LT', + nextDay: '[Yfory am] LT', + nextWeek: 'dddd [am] LT', + lastDay: '[Ddoe am] LT', + lastWeek: 'dddd [diwethaf am] LT', + sameElse: 'L' + }, + relativeTime: { + future: 'mewn %s', + past: '%s yn ôl', + s: 'ychydig eiliadau', + m: 'munud', + mm: '%d munud', + h: 'awr', + hh: '%d awr', + d: 'diwrnod', + dd: '%d diwrnod', + M: 'mis', + MM: '%d mis', + y: 'blwyddyn', + yy: '%d flynedd' + }, + dayOfMonthOrdinalParse: /\d{1,2}(fed|ain|af|il|ydd|ed|eg)/, + // traditional ordinal numbers above 31 are not commonly used in colloquial Welsh + ordinal: function (number) { + var b = number, + output = '', + lookup = [ + '', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed + 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed' // 11eg to 20fed + ]; + if (b > 20) { + if (b === 40 || b === 50 || b === 60 || b === 80 || b === 100) { + output = 'fed'; // not 30ain, 70ain or 90ain + } else { + output = 'ain'; } - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. + } else if (b > 0) { + output = lookup[b]; } - }); + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return cy; +return cy; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/da.js b/lib/javascripts/moment_locale/da.js old mode 100755 new mode 100644 index 70b4c0da43..357d870697 --- a/lib/javascripts/moment_locale/da.js +++ b/lib/javascripts/moment_locale/da.js @@ -1,60 +1,60 @@ //! moment.js locale configuration -//! locale : danish (da) +//! locale : Danish [da] //! author : Ulrik Nielsen : https://github.com/mrbase ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var da = moment.defineLocale('da', { - months : 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split('_'), - monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'), - weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'), - weekdaysShort : 'søn_man_tir_ons_tor_fre_lør'.split('_'), - weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY HH:mm', - LLLL : 'dddd [d.] D. MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[I dag kl.] LT', - nextDay : '[I morgen kl.] LT', - nextWeek : 'dddd [kl.] LT', - lastDay : '[I går kl.] LT', - lastWeek : '[sidste] dddd [kl] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'om %s', - past : '%s siden', - s : 'få sekunder', - m : 'et minut', - mm : '%d minutter', - h : 'en time', - hh : '%d timer', - d : 'en dag', - dd : '%d dage', - M : 'en måned', - MM : '%d måneder', - y : 'et år', - yy : '%d år' - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var da = moment.defineLocale('da', { + months : 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split('_'), + monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'), + weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'), + weekdaysShort : 'søn_man_tir_ons_tor_fre_lør'.split('_'), + weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY HH:mm', + LLLL : 'dddd [d.] D. MMMM YYYY [kl.] HH:mm' + }, + calendar : { + sameDay : '[i dag kl.] LT', + nextDay : '[i morgen kl.] LT', + nextWeek : 'på dddd [kl.] LT', + lastDay : '[i går kl.] LT', + lastWeek : '[i] dddd[s kl.] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'om %s', + past : '%s siden', + s : 'få sekunder', + m : 'et minut', + mm : '%d minutter', + h : 'en time', + hh : '%d timer', + d : 'en dag', + dd : '%d dage', + M : 'en måned', + MM : '%d måneder', + y : 'et år', + yy : '%d år' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return da; +return da; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/de-at.js b/lib/javascripts/moment_locale/de-at.js old mode 100755 new mode 100644 index 0ea81bcd38..aee7b8cb72 --- a/lib/javascripts/moment_locale/de-at.js +++ b/lib/javascripts/moment_locale/de-at.js @@ -1,5 +1,5 @@ //! moment.js locale configuration -//! locale : austrian german (de-at) +//! locale : German (Austria) [de-at] //! author : lluchs : https://github.com/lluchs //! author: Menelion Elensúle: https://github.com/Oire //! author : Martin Groller : https://github.com/MadMG @@ -8,72 +8,72 @@ ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function processRelativeTime(number, withoutSuffix, key, isFuture) { - var format = { - 'm': ['eine Minute', 'einer Minute'], - 'h': ['eine Stunde', 'einer Stunde'], - 'd': ['ein Tag', 'einem Tag'], - 'dd': [number + ' Tage', number + ' Tagen'], - 'M': ['ein Monat', 'einem Monat'], - 'MM': [number + ' Monate', number + ' Monaten'], - 'y': ['ein Jahr', 'einem Jahr'], - 'yy': [number + ' Jahre', number + ' Jahren'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 'm': ['eine Minute', 'einer Minute'], + 'h': ['eine Stunde', 'einer Stunde'], + 'd': ['ein Tag', 'einem Tag'], + 'dd': [number + ' Tage', number + ' Tagen'], + 'M': ['ein Monat', 'einem Monat'], + 'MM': [number + ' Monate', number + ' Monaten'], + 'y': ['ein Jahr', 'einem Jahr'], + 'yy': [number + ' Jahre', number + ' Jahren'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; +} + +var deAt = moment.defineLocale('de-at', { + months : 'Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), + monthsShort : 'Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'), + monthsParseExact : true, + weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), + weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'), + weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY HH:mm', + LLLL : 'dddd, D. MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[heute um] LT [Uhr]', + sameElse: 'L', + nextDay: '[morgen um] LT [Uhr]', + nextWeek: 'dddd [um] LT [Uhr]', + lastDay: '[gestern um] LT [Uhr]', + lastWeek: '[letzten] dddd [um] LT [Uhr]' + }, + relativeTime : { + future : 'in %s', + past : 'vor %s', + s : 'ein paar Sekunden', + m : processRelativeTime, + mm : '%d Minuten', + h : processRelativeTime, + hh : '%d Stunden', + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } +}); - var de_at = moment.defineLocale('de-at', { - months : 'Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), - monthsShort : 'Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), - weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'), - weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY HH:mm', - LLLL : 'dddd, D. MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[heute um] LT [Uhr]', - sameElse: 'L', - nextDay: '[morgen um] LT [Uhr]', - nextWeek: 'dddd [um] LT [Uhr]', - lastDay: '[gestern um] LT [Uhr]', - lastWeek: '[letzten] dddd [um] LT [Uhr]' - }, - relativeTime : { - future : 'in %s', - past : 'vor %s', - s : 'ein paar Sekunden', - m : processRelativeTime, - mm : '%d Minuten', - h : processRelativeTime, - hh : '%d Stunden', - d : processRelativeTime, - dd : processRelativeTime, - M : processRelativeTime, - MM : processRelativeTime, - y : processRelativeTime, - yy : processRelativeTime - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +return deAt; - return de_at; - -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/de-ch.js b/lib/javascripts/moment_locale/de-ch.js new file mode 100644 index 0000000000..e70f5e99f9 --- /dev/null +++ b/lib/javascripts/moment_locale/de-ch.js @@ -0,0 +1,78 @@ +//! moment.js locale configuration +//! locale : German (Switzerland) [de-ch] +//! author : sschueller : https://github.com/sschueller + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +// based on: https://www.bk.admin.ch/dokumentation/sprachen/04915/05016/index.html?lang=de# + +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 'm': ['eine Minute', 'einer Minute'], + 'h': ['eine Stunde', 'einer Stunde'], + 'd': ['ein Tag', 'einem Tag'], + 'dd': [number + ' Tage', number + ' Tagen'], + 'M': ['ein Monat', 'einem Monat'], + 'MM': [number + ' Monate', number + ' Monaten'], + 'y': ['ein Jahr', 'einem Jahr'], + 'yy': [number + ' Jahre', number + ' Jahren'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; +} + +var deCh = moment.defineLocale('de-ch', { + months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), + monthsShort : 'Jan._Febr._März_April_Mai_Juni_Juli_Aug._Sept._Okt._Nov._Dez.'.split('_'), + monthsParseExact : true, + weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), + weekdaysShort : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), + weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT: 'HH.mm', + LTS: 'HH.mm.ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY HH.mm', + LLLL : 'dddd, D. MMMM YYYY HH.mm' + }, + calendar : { + sameDay: '[heute um] LT [Uhr]', + sameElse: 'L', + nextDay: '[morgen um] LT [Uhr]', + nextWeek: 'dddd [um] LT [Uhr]', + lastDay: '[gestern um] LT [Uhr]', + lastWeek: '[letzten] dddd [um] LT [Uhr]' + }, + relativeTime : { + future : 'in %s', + past : 'vor %s', + s : 'ein paar Sekunden', + m : processRelativeTime, + mm : '%d Minuten', + h : processRelativeTime, + hh : '%d Stunden', + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return deCh; + +}))); diff --git a/lib/javascripts/moment_locale/de.js b/lib/javascripts/moment_locale/de.js old mode 100755 new mode 100644 index f1dfa9e2ff..a3263e8ca0 --- a/lib/javascripts/moment_locale/de.js +++ b/lib/javascripts/moment_locale/de.js @@ -1,5 +1,5 @@ //! moment.js locale configuration -//! locale : german (de) +//! locale : German [de] //! author : lluchs : https://github.com/lluchs //! author: Menelion Elensúle: https://github.com/Oire //! author : Mikolaj Dadela : https://github.com/mik01aj @@ -7,72 +7,72 @@ ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function processRelativeTime(number, withoutSuffix, key, isFuture) { - var format = { - 'm': ['eine Minute', 'einer Minute'], - 'h': ['eine Stunde', 'einer Stunde'], - 'd': ['ein Tag', 'einem Tag'], - 'dd': [number + ' Tage', number + ' Tagen'], - 'M': ['ein Monat', 'einem Monat'], - 'MM': [number + ' Monate', number + ' Monaten'], - 'y': ['ein Jahr', 'einem Jahr'], - 'yy': [number + ' Jahre', number + ' Jahren'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 'm': ['eine Minute', 'einer Minute'], + 'h': ['eine Stunde', 'einer Stunde'], + 'd': ['ein Tag', 'einem Tag'], + 'dd': [number + ' Tage', number + ' Tagen'], + 'M': ['ein Monat', 'einem Monat'], + 'MM': [number + ' Monate', number + ' Monaten'], + 'y': ['ein Jahr', 'einem Jahr'], + 'yy': [number + ' Jahre', number + ' Jahren'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; +} + +var de = moment.defineLocale('de', { + months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), + monthsShort : 'Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'), + monthsParseExact : true, + weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), + weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'), + weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY HH:mm', + LLLL : 'dddd, D. MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[heute um] LT [Uhr]', + sameElse: 'L', + nextDay: '[morgen um] LT [Uhr]', + nextWeek: 'dddd [um] LT [Uhr]', + lastDay: '[gestern um] LT [Uhr]', + lastWeek: '[letzten] dddd [um] LT [Uhr]' + }, + relativeTime : { + future : 'in %s', + past : 'vor %s', + s : 'ein paar Sekunden', + m : processRelativeTime, + mm : '%d Minuten', + h : processRelativeTime, + hh : '%d Stunden', + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } +}); - var de = moment.defineLocale('de', { - months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), - monthsShort : 'Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), - weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'), - weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY HH:mm', - LLLL : 'dddd, D. MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[heute um] LT [Uhr]', - sameElse: 'L', - nextDay: '[morgen um] LT [Uhr]', - nextWeek: 'dddd [um] LT [Uhr]', - lastDay: '[gestern um] LT [Uhr]', - lastWeek: '[letzten] dddd [um] LT [Uhr]' - }, - relativeTime : { - future : 'in %s', - past : 'vor %s', - s : 'ein paar Sekunden', - m : processRelativeTime, - mm : '%d Minuten', - h : processRelativeTime, - hh : '%d Stunden', - d : processRelativeTime, - dd : processRelativeTime, - M : processRelativeTime, - MM : processRelativeTime, - y : processRelativeTime, - yy : processRelativeTime - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +return de; - return de; - -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/dv.js b/lib/javascripts/moment_locale/dv.js old mode 100755 new mode 100644 index d29d66209f..065df78c36 --- a/lib/javascripts/moment_locale/dv.js +++ b/lib/javascripts/moment_locale/dv.js @@ -1,99 +1,100 @@ //! moment.js locale configuration -//! locale : dhivehi (dv) +//! locale : Maldivian [dv] //! author : Jawish Hameed : https://github.com/jawish ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var months = [ - 'ޖެނުއަރީ', - 'ފެބްރުއަރީ', - 'މާރިޗު', - 'އޭޕްރީލު', - 'މޭ', - 'ޖޫން', - 'ޖުލައި', - 'އޯގަސްޓު', - 'ސެޕްޓެމްބަރު', - 'އޮކްޓޯބަރު', - 'ނޮވެމްބަރު', - 'ޑިސެމްބަރު' - ], weekdays = [ - 'އާދިއްތަ', - 'ހޯމަ', - 'އަންގާރަ', - 'ބުދަ', - 'ބުރާސްފަތި', - 'ހުކުރު', - 'ހޮނިހިރު' - ]; +var months = [ + 'ޖެނުއަރީ', + 'ފެބްރުއަރީ', + 'މާރިޗު', + 'އޭޕްރީލު', + 'މޭ', + 'ޖޫން', + 'ޖުލައި', + 'އޯގަސްޓު', + 'ސެޕްޓެމްބަރު', + 'އޮކްޓޯބަރު', + 'ނޮވެމްބަރު', + 'ޑިސެމްބަރު' +]; +var weekdays = [ + 'އާދިއްތަ', + 'ހޯމަ', + 'އަންގާރަ', + 'ބުދަ', + 'ބުރާސްފަތި', + 'ހުކުރު', + 'ހޮނިހިރު' +]; - var dv = moment.defineLocale('dv', { - months : months, - monthsShort : months, - weekdays : weekdays, - weekdaysShort : weekdays, - weekdaysMin : 'އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި'.split('_'), - longDateFormat : { +var dv = moment.defineLocale('dv', { + months : months, + monthsShort : months, + weekdays : weekdays, + weekdaysShort : weekdays, + weekdaysMin : 'އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި'.split('_'), + longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'D/M/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - meridiemParse: /މކ|މފ/, - isPM : function (input) { - return 'މފ' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'މކ'; - } else { - return 'މފ'; - } - }, - calendar : { - sameDay : '[މިއަދު] LT', - nextDay : '[މާދަމާ] LT', - nextWeek : 'dddd LT', - lastDay : '[އިއްޔެ] LT', - lastWeek : '[ފާއިތުވި] dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : 'ތެރޭގައި %s', - past : 'ކުރިން %s', - s : 'ސިކުންތުކޮޅެއް', - m : 'މިނިޓެއް', - mm : 'މިނިޓު %d', - h : 'ގަޑިއިރެއް', - hh : 'ގަޑިއިރު %d', - d : 'ދުވަހެއް', - dd : 'ދުވަސް %d', - M : 'މަހެއް', - MM : 'މަސް %d', - y : 'އަހަރެއް', - yy : 'އަހަރު %d' - }, - preparse: function (string) { - return string.replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/,/g, '،'); - }, - week : { - dow : 7, // Sunday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'D/M/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + meridiemParse: /މކ|މފ/, + isPM : function (input) { + return 'މފ' === input; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'މކ'; + } else { + return 'މފ'; } - }); + }, + calendar : { + sameDay : '[މިއަދު] LT', + nextDay : '[މާދަމާ] LT', + nextWeek : 'dddd LT', + lastDay : '[އިއްޔެ] LT', + lastWeek : '[ފާއިތުވި] dddd LT', + sameElse : 'L' + }, + relativeTime : { + future : 'ތެރޭގައި %s', + past : 'ކުރިން %s', + s : 'ސިކުންތުކޮޅެއް', + m : 'މިނިޓެއް', + mm : 'މިނިޓު %d', + h : 'ގަޑިއިރެއް', + hh : 'ގަޑިއިރު %d', + d : 'ދުވަހެއް', + dd : 'ދުވަސް %d', + M : 'މަހެއް', + MM : 'މަސް %d', + y : 'އަހަރެއް', + yy : 'އަހަރު %d' + }, + preparse: function (string) { + return string.replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/,/g, '،'); + }, + week : { + dow : 7, // Sunday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); - return dv; +return dv; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/el.js b/lib/javascripts/moment_locale/el.js old mode 100755 new mode 100644 index d86666dcaf..bf3f4c95f7 --- a/lib/javascripts/moment_locale/el.js +++ b/lib/javascripts/moment_locale/el.js @@ -1,98 +1,100 @@ //! moment.js locale configuration -//! locale : modern greek (el) +//! locale : Greek [el] //! author : Aggelos Karalias : https://github.com/mehiel ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function isFunction(input) { - return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; - } +function isFunction(input) { + return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; +} - var el = moment.defineLocale('el', { - monthsNominativeEl : 'Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος'.split('_'), - monthsGenitiveEl : 'Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου'.split('_'), - months : function (momentToFormat, format) { - if (/D/.test(format.substring(0, format.indexOf('MMMM')))) { // if there is a day number before 'MMMM' - return this._monthsGenitiveEl[momentToFormat.month()]; - } else { - return this._monthsNominativeEl[momentToFormat.month()]; - } - }, - monthsShort : 'Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ'.split('_'), - weekdays : 'Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο'.split('_'), - weekdaysShort : 'Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ'.split('_'), - weekdaysMin : 'Κυ_Δε_Τρ_Τε_Πε_Πα_Σα'.split('_'), - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'μμ' : 'ΜΜ'; - } else { - return isLower ? 'πμ' : 'ΠΜ'; - } - }, - isPM : function (input) { - return ((input + '').toLowerCase()[0] === 'μ'); - }, - meridiemParse : /[ΠΜ]\.?Μ?\.?/i, - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendarEl : { - sameDay : '[Σήμερα {}] LT', - nextDay : '[Αύριο {}] LT', - nextWeek : 'dddd [{}] LT', - lastDay : '[Χθες {}] LT', - lastWeek : function () { - switch (this.day()) { - case 6: - return '[το προηγούμενο] dddd [{}] LT'; - default: - return '[την προηγούμενη] dddd [{}] LT'; - } - }, - sameElse : 'L' - }, - calendar : function (key, mom) { - var output = this._calendarEl[key], - hours = mom && mom.hours(); - if (isFunction(output)) { - output = output.apply(mom); - } - return output.replace('{}', (hours % 12 === 1 ? 'στη' : 'στις')); - }, - relativeTime : { - future : 'σε %s', - past : '%s πριν', - s : 'λίγα δευτερόλεπτα', - m : 'ένα λεπτό', - mm : '%d λεπτά', - h : 'μία ώρα', - hh : '%d ώρες', - d : 'μία μέρα', - dd : '%d μέρες', - M : 'ένας μήνας', - MM : '%d μήνες', - y : 'ένας χρόνος', - yy : '%d χρόνια' - }, - ordinalParse: /\d{1,2}η/, - ordinal: '%dη', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4st is the first week of the year. +var el = moment.defineLocale('el', { + monthsNominativeEl : 'Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος'.split('_'), + monthsGenitiveEl : 'Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου'.split('_'), + months : function (momentToFormat, format) { + if (!momentToFormat) { + return this._monthsNominativeEl; + } else if (/D/.test(format.substring(0, format.indexOf('MMMM')))) { // if there is a day number before 'MMMM' + return this._monthsGenitiveEl[momentToFormat.month()]; + } else { + return this._monthsNominativeEl[momentToFormat.month()]; } - }); + }, + monthsShort : 'Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ'.split('_'), + weekdays : 'Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο'.split('_'), + weekdaysShort : 'Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ'.split('_'), + weekdaysMin : 'Κυ_Δε_Τρ_Τε_Πε_Πα_Σα'.split('_'), + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'μμ' : 'ΜΜ'; + } else { + return isLower ? 'πμ' : 'ΠΜ'; + } + }, + isPM : function (input) { + return ((input + '').toLowerCase()[0] === 'μ'); + }, + meridiemParse : /[ΠΜ]\.?Μ?\.?/i, + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY h:mm A', + LLLL : 'dddd, D MMMM YYYY h:mm A' + }, + calendarEl : { + sameDay : '[Σήμερα {}] LT', + nextDay : '[Αύριο {}] LT', + nextWeek : 'dddd [{}] LT', + lastDay : '[Χθες {}] LT', + lastWeek : function () { + switch (this.day()) { + case 6: + return '[το προηγούμενο] dddd [{}] LT'; + default: + return '[την προηγούμενη] dddd [{}] LT'; + } + }, + sameElse : 'L' + }, + calendar : function (key, mom) { + var output = this._calendarEl[key], + hours = mom && mom.hours(); + if (isFunction(output)) { + output = output.apply(mom); + } + return output.replace('{}', (hours % 12 === 1 ? 'στη' : 'στις')); + }, + relativeTime : { + future : 'σε %s', + past : '%s πριν', + s : 'λίγα δευτερόλεπτα', + m : 'ένα λεπτό', + mm : '%d λεπτά', + h : 'μία ώρα', + hh : '%d ώρες', + d : 'μία μέρα', + dd : '%d μέρες', + M : 'ένας μήνας', + MM : '%d μήνες', + y : 'ένας χρόνος', + yy : '%d χρόνια' + }, + dayOfMonthOrdinalParse: /\d{1,2}η/, + ordinal: '%dη', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4st is the first week of the year. + } +}); - return el; +return el; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/en-au.js b/lib/javascripts/moment_locale/en-au.js old mode 100755 new mode 100644 index 58608c1834..e8a8fdc96f --- a/lib/javascripts/moment_locale/en-au.js +++ b/lib/javascripts/moment_locale/en-au.js @@ -1,66 +1,67 @@ //! moment.js locale configuration -//! locale : australian english (en-au) +//! locale : English (Australia) [en-au] +//! author : Jared Morse : https://github.com/jarcoal ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var en_au = moment.defineLocale('en-au', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - ordinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var enAu = moment.defineLocale('en-au', { + months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY h:mm A', + LLLL : 'dddd, D MMMM YYYY h:mm A' + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, + dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return en_au; +return enAu; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/en-ca.js b/lib/javascripts/moment_locale/en-ca.js old mode 100755 new mode 100644 index 3e3bc4fe57..3629bf1ab1 --- a/lib/javascripts/moment_locale/en-ca.js +++ b/lib/javascripts/moment_locale/en-ca.js @@ -1,63 +1,63 @@ //! moment.js locale configuration -//! locale : canadian english (en-ca) +//! locale : English (Canada) [en-ca] //! author : Jonathan Abourbih : https://github.com/jonbca ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var en_ca = moment.defineLocale('en-ca', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'YYYY-MM-DD', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY h:mm A', - LLLL : 'dddd, MMMM D, YYYY h:mm A' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - ordinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); +var enCa = moment.defineLocale('en-ca', { + months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'YYYY-MM-DD', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY h:mm A', + LLLL : 'dddd, MMMM D, YYYY h:mm A' + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, + dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } +}); - return en_ca; +return enCa; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/en-gb.js b/lib/javascripts/moment_locale/en-gb.js old mode 100755 new mode 100644 index 47b2c209b7..fe48a78cd0 --- a/lib/javascripts/moment_locale/en-gb.js +++ b/lib/javascripts/moment_locale/en-gb.js @@ -1,67 +1,67 @@ //! moment.js locale configuration -//! locale : great britain english (en-gb) +//! locale : English (United Kingdom) [en-gb] //! author : Chris Gedrim : https://github.com/chrisgedrim ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var en_gb = moment.defineLocale('en-gb', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - ordinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var enGb = moment.defineLocale('en-gb', { + months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, + dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return en_gb; +return enGb; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/en-ie.js b/lib/javascripts/moment_locale/en-ie.js old mode 100755 new mode 100644 index c0ff10c405..bb0cccef67 --- a/lib/javascripts/moment_locale/en-ie.js +++ b/lib/javascripts/moment_locale/en-ie.js @@ -1,67 +1,67 @@ //! moment.js locale configuration -//! locale : Irish english (en-ie) +//! locale : English (Ireland) [en-ie] //! author : Chris Cartlidge : https://github.com/chriscartlidge ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var en_ie = moment.defineLocale('en-ie', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - ordinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var enIe = moment.defineLocale('en-ie', { + months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD-MM-YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, + dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return en_ie; +return enIe; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/en-nz.js b/lib/javascripts/moment_locale/en-nz.js old mode 100755 new mode 100644 index 14a50ea4bd..700523193c --- a/lib/javascripts/moment_locale/en-nz.js +++ b/lib/javascripts/moment_locale/en-nz.js @@ -1,66 +1,67 @@ //! moment.js locale configuration -//! locale : New Zealand english (en-nz) +//! locale : English (New Zealand) [en-nz] +//! author : Luke McGregor : https://github.com/lukemcgregor ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var en_nz = moment.defineLocale('en-nz', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - ordinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var enNz = moment.defineLocale('en-nz', { + months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY h:mm A', + LLLL : 'dddd, D MMMM YYYY h:mm A' + }, + calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, + dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return en_nz; +return enNz; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/eo.js b/lib/javascripts/moment_locale/eo.js old mode 100755 new mode 100644 index 92772dffa4..8001c83c69 --- a/lib/javascripts/moment_locale/eo.js +++ b/lib/javascripts/moment_locale/eo.js @@ -1,73 +1,73 @@ //! moment.js locale configuration -//! locale : esperanto (eo) +//! locale : Esperanto [eo] //! author : Colin Dean : https://github.com/colindean -//! komento: Mi estas malcerta se mi korekte traktis akuzativojn en tiu traduko. -//! Se ne, bonvolu korekti kaj avizi min por ke mi povas lerni! +//! author : Mia Nordentoft Imperatori : https://github.com/miestasmia +//! comment : miestasmia corrected the translation by colindean ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var eo = moment.defineLocale('eo', { - months : 'januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro'.split('_'), - monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec'.split('_'), - weekdays : 'Dimanĉo_Lundo_Mardo_Merkredo_Ĵaŭdo_Vendredo_Sabato'.split('_'), - weekdaysShort : 'Dim_Lun_Mard_Merk_Ĵaŭ_Ven_Sab'.split('_'), - weekdaysMin : 'Di_Lu_Ma_Me_Ĵa_Ve_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'D[-an de] MMMM, YYYY', - LLL : 'D[-an de] MMMM, YYYY HH:mm', - LLLL : 'dddd, [la] D[-an de] MMMM, YYYY HH:mm' - }, - meridiemParse: /[ap]\.t\.m/i, - isPM: function (input) { - return input.charAt(0).toLowerCase() === 'p'; - }, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'p.t.m.' : 'P.T.M.'; - } else { - return isLower ? 'a.t.m.' : 'A.T.M.'; - } - }, - calendar : { - sameDay : '[Hodiaŭ je] LT', - nextDay : '[Morgaŭ je] LT', - nextWeek : 'dddd [je] LT', - lastDay : '[Hieraŭ je] LT', - lastWeek : '[pasinta] dddd [je] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'je %s', - past : 'antaŭ %s', - s : 'sekundoj', - m : 'minuto', - mm : '%d minutoj', - h : 'horo', - hh : '%d horoj', - d : 'tago',//ne 'diurno', ĉar estas uzita por proksimumo - dd : '%d tagoj', - M : 'monato', - MM : '%d monatoj', - y : 'jaro', - yy : '%d jaroj' - }, - ordinalParse: /\d{1,2}a/, - ordinal : '%da', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var eo = moment.defineLocale('eo', { + months : 'januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro'.split('_'), + monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec'.split('_'), + weekdays : 'dimanĉo_lundo_mardo_merkredo_ĵaŭdo_vendredo_sabato'.split('_'), + weekdaysShort : 'dim_lun_mard_merk_ĵaŭ_ven_sab'.split('_'), + weekdaysMin : 'di_lu_ma_me_ĵa_ve_sa'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY-MM-DD', + LL : 'D[-a de] MMMM, YYYY', + LLL : 'D[-a de] MMMM, YYYY HH:mm', + LLLL : 'dddd, [la] D[-a de] MMMM, YYYY HH:mm' + }, + meridiemParse: /[ap]\.t\.m/i, + isPM: function (input) { + return input.charAt(0).toLowerCase() === 'p'; + }, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'p.t.m.' : 'P.T.M.'; + } else { + return isLower ? 'a.t.m.' : 'A.T.M.'; } - }); + }, + calendar : { + sameDay : '[Hodiaŭ je] LT', + nextDay : '[Morgaŭ je] LT', + nextWeek : 'dddd [je] LT', + lastDay : '[Hieraŭ je] LT', + lastWeek : '[pasinta] dddd [je] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'post %s', + past : 'antaŭ %s', + s : 'sekundoj', + m : 'minuto', + mm : '%d minutoj', + h : 'horo', + hh : '%d horoj', + d : 'tago',//ne 'diurno', ĉar estas uzita por proksimumo + dd : '%d tagoj', + M : 'monato', + MM : '%d monatoj', + y : 'jaro', + yy : '%d jaroj' + }, + dayOfMonthOrdinalParse: /\d{1,2}a/, + ordinal : '%da', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return eo; +return eo; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/es-do.js b/lib/javascripts/moment_locale/es-do.js new file mode 100644 index 0000000000..1af11adf37 --- /dev/null +++ b/lib/javascripts/moment_locale/es-do.js @@ -0,0 +1,82 @@ +//! moment.js locale configuration +//! locale : Spanish (Dominican Republic) [es-do] + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'); +var monthsShort = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'); + +var esDo = moment.defineLocale('es-do', { + months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'), + monthsShort : function (m, format) { + if (!m) { + return monthsShortDot; + } else if (/-MMM-/.test(format)) { + return monthsShort[m.month()]; + } else { + return monthsShortDot[m.month()]; + } + }, + monthsParseExact : true, + weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'), + weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'), + weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D [de] MMMM [de] YYYY', + LLL : 'D [de] MMMM [de] YYYY h:mm A', + LLLL : 'dddd, D [de] MMMM [de] YYYY h:mm A' + }, + calendar : { + sameDay : function () { + return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextDay : function () { + return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextWeek : function () { + return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastDay : function () { + return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastWeek : function () { + return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : 'en %s', + past : 'hace %s', + s : 'unos segundos', + m : 'un minuto', + mm : '%d minutos', + h : 'una hora', + hh : '%d horas', + d : 'un día', + dd : '%d días', + M : 'un mes', + MM : '%d meses', + y : 'un año', + yy : '%d años' + }, + dayOfMonthOrdinalParse : /\d{1,2}º/, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return esDo; + +}))); diff --git a/lib/javascripts/moment_locale/es.js b/lib/javascripts/moment_locale/es.js old mode 100755 new mode 100644 index 555e94bf2b..c0dea59abd --- a/lib/javascripts/moment_locale/es.js +++ b/lib/javascripts/moment_locale/es.js @@ -1,81 +1,83 @@ //! moment.js locale configuration -//! locale : spanish (es) +//! locale : Spanish [es] //! author : Julio Napurí : https://github.com/julionc ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'), - monthsShort = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'); +var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'); +var monthsShort = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'); - var es = moment.defineLocale('es', { - months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'), - monthsShort : function (m, format) { - if (/-MMM-/.test(format)) { - return monthsShort[m.month()]; - } else { - return monthsShortDot[m.month()]; - } - }, - monthsParseExact : true, - weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'), - weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'), - weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY H:mm', - LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm' - }, - calendar : { - sameDay : function () { - return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextDay : function () { - return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextWeek : function () { - return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastDay : function () { - return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastWeek : function () { - return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'en %s', - past : 'hace %s', - s : 'unos segundos', - m : 'un minuto', - mm : '%d minutos', - h : 'una hora', - hh : '%d horas', - d : 'un día', - dd : '%d días', - M : 'un mes', - MM : '%d meses', - y : 'un año', - yy : '%d años' - }, - ordinalParse : /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. +var es = moment.defineLocale('es', { + months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'), + monthsShort : function (m, format) { + if (!m) { + return monthsShortDot; + } else if (/-MMM-/.test(format)) { + return monthsShort[m.month()]; + } else { + return monthsShortDot[m.month()]; } - }); + }, + monthsParseExact : true, + weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'), + weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'), + weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D [de] MMMM [de] YYYY', + LLL : 'D [de] MMMM [de] YYYY H:mm', + LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm' + }, + calendar : { + sameDay : function () { + return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextDay : function () { + return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + nextWeek : function () { + return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastDay : function () { + return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + lastWeek : function () { + return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : 'en %s', + past : 'hace %s', + s : 'unos segundos', + m : 'un minuto', + mm : '%d minutos', + h : 'una hora', + hh : '%d horas', + d : 'un día', + dd : '%d días', + M : 'un mes', + MM : '%d meses', + y : 'un año', + yy : '%d años' + }, + dayOfMonthOrdinalParse : /\d{1,2}º/, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return es; +return es; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/et.js b/lib/javascripts/moment_locale/et.js old mode 100755 new mode 100644 index 09043bfad6..3a759e9e89 --- a/lib/javascripts/moment_locale/et.js +++ b/lib/javascripts/moment_locale/et.js @@ -1,80 +1,80 @@ //! moment.js locale configuration -//! locale : estonian (et) +//! locale : Estonian [et] //! author : Henry Kehlmann : https://github.com/madhenry //! improvements : Illimar Tambek : https://github.com/ragulka ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function processRelativeTime(number, withoutSuffix, key, isFuture) { - var format = { - 's' : ['mõne sekundi', 'mõni sekund', 'paar sekundit'], - 'm' : ['ühe minuti', 'üks minut'], - 'mm': [number + ' minuti', number + ' minutit'], - 'h' : ['ühe tunni', 'tund aega', 'üks tund'], - 'hh': [number + ' tunni', number + ' tundi'], - 'd' : ['ühe päeva', 'üks päev'], - 'M' : ['kuu aja', 'kuu aega', 'üks kuu'], - 'MM': [number + ' kuu', number + ' kuud'], - 'y' : ['ühe aasta', 'aasta', 'üks aasta'], - 'yy': [number + ' aasta', number + ' aastat'] - }; - if (withoutSuffix) { - return format[key][2] ? format[key][2] : format[key][1]; - } - return isFuture ? format[key][0] : format[key][1]; +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 's' : ['mõne sekundi', 'mõni sekund', 'paar sekundit'], + 'm' : ['ühe minuti', 'üks minut'], + 'mm': [number + ' minuti', number + ' minutit'], + 'h' : ['ühe tunni', 'tund aega', 'üks tund'], + 'hh': [number + ' tunni', number + ' tundi'], + 'd' : ['ühe päeva', 'üks päev'], + 'M' : ['kuu aja', 'kuu aega', 'üks kuu'], + 'MM': [number + ' kuu', number + ' kuud'], + 'y' : ['ühe aasta', 'aasta', 'üks aasta'], + 'yy': [number + ' aasta', number + ' aastat'] + }; + if (withoutSuffix) { + return format[key][2] ? format[key][2] : format[key][1]; } + return isFuture ? format[key][0] : format[key][1]; +} - var et = moment.defineLocale('et', { - months : 'jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember'.split('_'), - monthsShort : 'jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets'.split('_'), - weekdays : 'pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev'.split('_'), - weekdaysShort : 'P_E_T_K_N_R_L'.split('_'), - weekdaysMin : 'P_E_T_K_N_R_L'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[Täna,] LT', - nextDay : '[Homme,] LT', - nextWeek : '[Järgmine] dddd LT', - lastDay : '[Eile,] LT', - lastWeek : '[Eelmine] dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s pärast', - past : '%s tagasi', - s : processRelativeTime, - m : processRelativeTime, - mm : processRelativeTime, - h : processRelativeTime, - hh : processRelativeTime, - d : processRelativeTime, - dd : '%d päeva', - M : processRelativeTime, - MM : processRelativeTime, - y : processRelativeTime, - yy : processRelativeTime - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var et = moment.defineLocale('et', { + months : 'jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember'.split('_'), + monthsShort : 'jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets'.split('_'), + weekdays : 'pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev'.split('_'), + weekdaysShort : 'P_E_T_K_N_R_L'.split('_'), + weekdaysMin : 'P_E_T_K_N_R_L'.split('_'), + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY H:mm', + LLLL : 'dddd, D. MMMM YYYY H:mm' + }, + calendar : { + sameDay : '[Täna,] LT', + nextDay : '[Homme,] LT', + nextWeek : '[Järgmine] dddd LT', + lastDay : '[Eile,] LT', + lastWeek : '[Eelmine] dddd LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s pärast', + past : '%s tagasi', + s : processRelativeTime, + m : processRelativeTime, + mm : processRelativeTime, + h : processRelativeTime, + hh : processRelativeTime, + d : processRelativeTime, + dd : '%d päeva', + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return et; +return et; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/eu.js b/lib/javascripts/moment_locale/eu.js old mode 100755 new mode 100644 index 7383f89b16..e2021cfa1a --- a/lib/javascripts/moment_locale/eu.js +++ b/lib/javascripts/moment_locale/eu.js @@ -1,66 +1,66 @@ //! moment.js locale configuration -//! locale : euskara (eu) +//! locale : Basque [eu] //! author : Eneko Illarramendi : https://github.com/eillarra ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var eu = moment.defineLocale('eu', { - months : 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'), - monthsShort : 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'), - monthsParseExact : true, - weekdays : 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'), - weekdaysShort : 'ig._al._ar._az._og._ol._lr.'.split('_'), - weekdaysMin : 'ig_al_ar_az_og_ol_lr'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'YYYY[ko] MMMM[ren] D[a]', - LLL : 'YYYY[ko] MMMM[ren] D[a] HH:mm', - LLLL : 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm', - l : 'YYYY-M-D', - ll : 'YYYY[ko] MMM D[a]', - lll : 'YYYY[ko] MMM D[a] HH:mm', - llll : 'ddd, YYYY[ko] MMM D[a] HH:mm' - }, - calendar : { - sameDay : '[gaur] LT[etan]', - nextDay : '[bihar] LT[etan]', - nextWeek : 'dddd LT[etan]', - lastDay : '[atzo] LT[etan]', - lastWeek : '[aurreko] dddd LT[etan]', - sameElse : 'L' - }, - relativeTime : { - future : '%s barru', - past : 'duela %s', - s : 'segundo batzuk', - m : 'minutu bat', - mm : '%d minutu', - h : 'ordu bat', - hh : '%d ordu', - d : 'egun bat', - dd : '%d egun', - M : 'hilabete bat', - MM : '%d hilabete', - y : 'urte bat', - yy : '%d urte' - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); +var eu = moment.defineLocale('eu', { + months : 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'), + monthsShort : 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'), + monthsParseExact : true, + weekdays : 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'), + weekdaysShort : 'ig._al._ar._az._og._ol._lr.'.split('_'), + weekdaysMin : 'ig_al_ar_az_og_ol_lr'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY-MM-DD', + LL : 'YYYY[ko] MMMM[ren] D[a]', + LLL : 'YYYY[ko] MMMM[ren] D[a] HH:mm', + LLLL : 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm', + l : 'YYYY-M-D', + ll : 'YYYY[ko] MMM D[a]', + lll : 'YYYY[ko] MMM D[a] HH:mm', + llll : 'ddd, YYYY[ko] MMM D[a] HH:mm' + }, + calendar : { + sameDay : '[gaur] LT[etan]', + nextDay : '[bihar] LT[etan]', + nextWeek : 'dddd LT[etan]', + lastDay : '[atzo] LT[etan]', + lastWeek : '[aurreko] dddd LT[etan]', + sameElse : 'L' + }, + relativeTime : { + future : '%s barru', + past : 'duela %s', + s : 'segundo batzuk', + m : 'minutu bat', + mm : '%d minutu', + h : 'ordu bat', + hh : '%d ordu', + d : 'egun bat', + dd : '%d egun', + M : 'hilabete bat', + MM : '%d hilabete', + y : 'urte bat', + yy : '%d urte' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return eu; +return eu; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/fa.js b/lib/javascripts/moment_locale/fa.js old mode 100755 new mode 100644 index e2bd7b0b5c..e6e5619d09 --- a/lib/javascripts/moment_locale/fa.js +++ b/lib/javascripts/moment_locale/fa.js @@ -1,106 +1,107 @@ //! moment.js locale configuration -//! locale : Persian (fa) +//! locale : Persian [fa] //! author : Ebrahim Byagowi : https://github.com/ebraminio ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '۱', - '2': '۲', - '3': '۳', - '4': '۴', - '5': '۵', - '6': '۶', - '7': '۷', - '8': '۸', - '9': '۹', - '0': '۰' - }, numberMap = { - '۱': '1', - '۲': '2', - '۳': '3', - '۴': '4', - '۵': '5', - '۶': '6', - '۷': '7', - '۸': '8', - '۹': '9', - '۰': '0' - }; +var symbolMap = { + '1': '۱', + '2': '۲', + '3': '۳', + '4': '۴', + '5': '۵', + '6': '۶', + '7': '۷', + '8': '۸', + '9': '۹', + '0': '۰' +}; +var numberMap = { + '۱': '1', + '۲': '2', + '۳': '3', + '۴': '4', + '۵': '5', + '۶': '6', + '۷': '7', + '۸': '8', + '۹': '9', + '۰': '0' +}; - var fa = moment.defineLocale('fa', { - months : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), - monthsShort : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), - weekdays : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), - weekdaysShort : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), - weekdaysMin : 'ی_د_س_چ_پ_ج_ش'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - meridiemParse: /قبل از ظهر|بعد از ظهر/, - isPM: function (input) { - return /بعد از ظهر/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'قبل از ظهر'; - } else { - return 'بعد از ظهر'; - } - }, - calendar : { - sameDay : '[امروز ساعت] LT', - nextDay : '[فردا ساعت] LT', - nextWeek : 'dddd [ساعت] LT', - lastDay : '[دیروز ساعت] LT', - lastWeek : 'dddd [پیش] [ساعت] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'در %s', - past : '%s پیش', - s : 'چندین ثانیه', - m : 'یک دقیقه', - mm : '%d دقیقه', - h : 'یک ساعت', - hh : '%d ساعت', - d : 'یک روز', - dd : '%d روز', - M : 'یک ماه', - MM : '%d ماه', - y : 'یک سال', - yy : '%d سال' - }, - preparse: function (string) { - return string.replace(/[۰-۹]/g, function (match) { - return numberMap[match]; - }).replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }).replace(/,/g, '،'); - }, - ordinalParse: /\d{1,2}م/, - ordinal : '%dم', - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. +var fa = moment.defineLocale('fa', { + months : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), + monthsShort : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), + weekdays : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), + weekdaysShort : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), + weekdaysMin : 'ی_د_س_چ_پ_ج_ش'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + meridiemParse: /قبل از ظهر|بعد از ظهر/, + isPM: function (input) { + return /بعد از ظهر/.test(input); + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'قبل از ظهر'; + } else { + return 'بعد از ظهر'; } - }); + }, + calendar : { + sameDay : '[امروز ساعت] LT', + nextDay : '[فردا ساعت] LT', + nextWeek : 'dddd [ساعت] LT', + lastDay : '[دیروز ساعت] LT', + lastWeek : 'dddd [پیش] [ساعت] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'در %s', + past : '%s پیش', + s : 'چند ثانیه', + m : 'یک دقیقه', + mm : '%d دقیقه', + h : 'یک ساعت', + hh : '%d ساعت', + d : 'یک روز', + dd : '%d روز', + M : 'یک ماه', + MM : '%d ماه', + y : 'یک سال', + yy : '%d سال' + }, + preparse: function (string) { + return string.replace(/[۰-۹]/g, function (match) { + return numberMap[match]; + }).replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }).replace(/,/g, '،'); + }, + dayOfMonthOrdinalParse: /\d{1,2}م/, + ordinal : '%dم', + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); - return fa; +return fa; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/fi.js b/lib/javascripts/moment_locale/fi.js old mode 100755 new mode 100644 index 4f9161bdd3..3eeef5c14a --- a/lib/javascripts/moment_locale/fi.js +++ b/lib/javascripts/moment_locale/fi.js @@ -1,23 +1,23 @@ //! moment.js locale configuration -//! locale : finnish (fi) +//! locale : Finnish [fi] //! author : Tarmo Aidantausta : https://github.com/bleadof ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' '), - numbersFuture = [ - 'nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden', - numbersPast[7], numbersPast[8], numbersPast[9] - ]; - function translate(number, withoutSuffix, key, isFuture) { - var result = ''; - switch (key) { +var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' '); +var numbersFuture = [ + 'nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden', + numbersPast[7], numbersPast[8], numbersPast[9] + ]; +function translate(number, withoutSuffix, key, isFuture) { + var result = ''; + switch (key) { case 's': return isFuture ? 'muutaman sekunnin' : 'muutama sekunti'; case 'm': @@ -45,63 +45,63 @@ case 'yy': result = isFuture ? 'vuoden' : 'vuotta'; break; - } - result = verbalNumber(number, isFuture) + ' ' + result; - return result; } - function verbalNumber(number, isFuture) { - return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number; + result = verbalNumber(number, isFuture) + ' ' + result; + return result; +} +function verbalNumber(number, isFuture) { + return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number; +} + +var fi = moment.defineLocale('fi', { + months : 'tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu'.split('_'), + monthsShort : 'tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu'.split('_'), + weekdays : 'sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai'.split('_'), + weekdaysShort : 'su_ma_ti_ke_to_pe_la'.split('_'), + weekdaysMin : 'su_ma_ti_ke_to_pe_la'.split('_'), + longDateFormat : { + LT : 'HH.mm', + LTS : 'HH.mm.ss', + L : 'DD.MM.YYYY', + LL : 'Do MMMM[ta] YYYY', + LLL : 'Do MMMM[ta] YYYY, [klo] HH.mm', + LLLL : 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm', + l : 'D.M.YYYY', + ll : 'Do MMM YYYY', + lll : 'Do MMM YYYY, [klo] HH.mm', + llll : 'ddd, Do MMM YYYY, [klo] HH.mm' + }, + calendar : { + sameDay : '[tänään] [klo] LT', + nextDay : '[huomenna] [klo] LT', + nextWeek : 'dddd [klo] LT', + lastDay : '[eilen] [klo] LT', + lastWeek : '[viime] dddd[na] [klo] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s päästä', + past : '%s sitten', + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } +}); - var fi = moment.defineLocale('fi', { - months : 'tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu'.split('_'), - monthsShort : 'tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu'.split('_'), - weekdays : 'sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai'.split('_'), - weekdaysShort : 'su_ma_ti_ke_to_pe_la'.split('_'), - weekdaysMin : 'su_ma_ti_ke_to_pe_la'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD.MM.YYYY', - LL : 'Do MMMM[ta] YYYY', - LLL : 'Do MMMM[ta] YYYY, [klo] HH.mm', - LLLL : 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm', - l : 'D.M.YYYY', - ll : 'Do MMM YYYY', - lll : 'Do MMM YYYY, [klo] HH.mm', - llll : 'ddd, Do MMM YYYY, [klo] HH.mm' - }, - calendar : { - sameDay : '[tänään] [klo] LT', - nextDay : '[huomenna] [klo] LT', - nextWeek : 'dddd [klo] LT', - lastDay : '[eilen] [klo] LT', - lastWeek : '[viime] dddd[na] [klo] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s päästä', - past : '%s sitten', - s : translate, - m : translate, - mm : translate, - h : translate, - hh : translate, - d : translate, - dd : translate, - M : translate, - MM : translate, - y : translate, - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +return fi; - return fi; - -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/fo.js b/lib/javascripts/moment_locale/fo.js old mode 100755 new mode 100644 index 460b6cd81c..eec43bc6b4 --- a/lib/javascripts/moment_locale/fo.js +++ b/lib/javascripts/moment_locale/fo.js @@ -1,60 +1,60 @@ //! moment.js locale configuration -//! locale : faroese (fo) +//! locale : Faroese [fo] //! author : Ragnar Johannesen : https://github.com/ragnar123 ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var fo = moment.defineLocale('fo', { - months : 'januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember'.split('_'), - monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'), - weekdays : 'sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur'.split('_'), - weekdaysShort : 'sun_mán_týs_mik_hós_frí_ley'.split('_'), - weekdaysMin : 'su_má_tý_mi_hó_fr_le'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D. MMMM, YYYY HH:mm' - }, - calendar : { - sameDay : '[Í dag kl.] LT', - nextDay : '[Í morgin kl.] LT', - nextWeek : 'dddd [kl.] LT', - lastDay : '[Í gjár kl.] LT', - lastWeek : '[síðstu] dddd [kl] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'um %s', - past : '%s síðani', - s : 'fá sekund', - m : 'ein minutt', - mm : '%d minuttir', - h : 'ein tími', - hh : '%d tímar', - d : 'ein dagur', - dd : '%d dagar', - M : 'ein mánaði', - MM : '%d mánaðir', - y : 'eitt ár', - yy : '%d ár' - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var fo = moment.defineLocale('fo', { + months : 'januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember'.split('_'), + monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'), + weekdays : 'sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur'.split('_'), + weekdaysShort : 'sun_mán_týs_mik_hós_frí_ley'.split('_'), + weekdaysMin : 'su_má_tý_mi_hó_fr_le'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D. MMMM, YYYY HH:mm' + }, + calendar : { + sameDay : '[Í dag kl.] LT', + nextDay : '[Í morgin kl.] LT', + nextWeek : 'dddd [kl.] LT', + lastDay : '[Í gjár kl.] LT', + lastWeek : '[síðstu] dddd [kl] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'um %s', + past : '%s síðani', + s : 'fá sekund', + m : 'ein minutt', + mm : '%d minuttir', + h : 'ein tími', + hh : '%d tímar', + d : 'ein dagur', + dd : '%d dagar', + M : 'ein mánaði', + MM : '%d mánaðir', + y : 'eitt ár', + yy : '%d ár' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return fo; +return fo; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/fr-ca.js b/lib/javascripts/moment_locale/fr-ca.js old mode 100755 new mode 100644 index 52108b886d..49e61d299d --- a/lib/javascripts/moment_locale/fr-ca.js +++ b/lib/javascripts/moment_locale/fr-ca.js @@ -1,60 +1,74 @@ //! moment.js locale configuration -//! locale : canadian french (fr-ca) +//! locale : French (Canada) [fr-ca] //! author : Jonathan Abourbih : https://github.com/jonbca ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var fr_ca = moment.defineLocale('fr-ca', { - months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact : true, - weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Aujourd\'hui à] LT', - nextDay: '[Demain à] LT', - nextWeek: 'dddd [à] LT', - lastDay: '[Hier à] LT', - lastWeek: 'dddd [dernier à] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'dans %s', - past : 'il y a %s', - s : 'quelques secondes', - m : 'une minute', - mm : '%d minutes', - h : 'une heure', - hh : '%d heures', - d : 'un jour', - dd : '%d jours', - M : 'un mois', - MM : '%d mois', - y : 'un an', - yy : '%d ans' - }, - ordinalParse: /\d{1,2}(er|e)/, - ordinal : function (number) { - return number + (number === 1 ? 'er' : 'e'); +var frCa = moment.defineLocale('fr-ca', { + months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), + monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), + monthsParseExact : true, + weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), + weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), + weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY-MM-DD', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Aujourd’hui à] LT', + nextDay : '[Demain à] LT', + nextWeek : 'dddd [à] LT', + lastDay : '[Hier à] LT', + lastWeek : 'dddd [dernier à] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'dans %s', + past : 'il y a %s', + s : 'quelques secondes', + m : 'une minute', + mm : '%d minutes', + h : 'une heure', + hh : '%d heures', + d : 'un jour', + dd : '%d jours', + M : 'un mois', + MM : '%d mois', + y : 'un an', + yy : '%d ans' + }, + dayOfMonthOrdinalParse: /\d{1,2}(er|e)/, + ordinal : function (number, period) { + switch (period) { + // Words with masculine grammatical gender: mois, trimestre, jour + default: + case 'M': + case 'Q': + case 'D': + case 'DDD': + case 'd': + return number + (number === 1 ? 'er' : 'e'); + + // Words with feminine grammatical gender: semaine + case 'w': + case 'W': + return number + (number === 1 ? 're' : 'e'); } - }); + } +}); - return fr_ca; +return frCa; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/fr-ch.js b/lib/javascripts/moment_locale/fr-ch.js old mode 100755 new mode 100644 index 876de40a75..38bf858dc4 --- a/lib/javascripts/moment_locale/fr-ch.js +++ b/lib/javascripts/moment_locale/fr-ch.js @@ -1,64 +1,78 @@ //! moment.js locale configuration -//! locale : swiss french (fr) +//! locale : French (Switzerland) [fr-ch] //! author : Gaspard Bucher : https://github.com/gaspard ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var fr_ch = moment.defineLocale('fr-ch', { - months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact : true, - weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Aujourd\'hui à] LT', - nextDay: '[Demain à] LT', - nextWeek: 'dddd [à] LT', - lastDay: '[Hier à] LT', - lastWeek: 'dddd [dernier à] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'dans %s', - past : 'il y a %s', - s : 'quelques secondes', - m : 'une minute', - mm : '%d minutes', - h : 'une heure', - hh : '%d heures', - d : 'un jour', - dd : '%d jours', - M : 'un mois', - MM : '%d mois', - y : 'un an', - yy : '%d ans' - }, - ordinalParse: /\d{1,2}(er|e)/, - ordinal : function (number) { - return number + (number === 1 ? 'er' : 'e'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. +var frCh = moment.defineLocale('fr-ch', { + months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), + monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), + monthsParseExact : true, + weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), + weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), + weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Aujourd’hui à] LT', + nextDay : '[Demain à] LT', + nextWeek : 'dddd [à] LT', + lastDay : '[Hier à] LT', + lastWeek : 'dddd [dernier à] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'dans %s', + past : 'il y a %s', + s : 'quelques secondes', + m : 'une minute', + mm : '%d minutes', + h : 'une heure', + hh : '%d heures', + d : 'un jour', + dd : '%d jours', + M : 'un mois', + MM : '%d mois', + y : 'un an', + yy : '%d ans' + }, + dayOfMonthOrdinalParse: /\d{1,2}(er|e)/, + ordinal : function (number, period) { + switch (period) { + // Words with masculine grammatical gender: mois, trimestre, jour + default: + case 'M': + case 'Q': + case 'D': + case 'DDD': + case 'd': + return number + (number === 1 ? 'er' : 'e'); + + // Words with feminine grammatical gender: semaine + case 'w': + case 'W': + return number + (number === 1 ? 're' : 'e'); } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return fr_ch; +return frCh; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/fr.js b/lib/javascripts/moment_locale/fr.js old mode 100755 new mode 100644 index d08ebf1ba5..7e4cfbce8f --- a/lib/javascripts/moment_locale/fr.js +++ b/lib/javascripts/moment_locale/fr.js @@ -1,64 +1,83 @@ //! moment.js locale configuration -//! locale : french (fr) +//! locale : French [fr] //! author : John Fischer : https://github.com/jfroffice ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var fr = moment.defineLocale('fr', { - months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact : true, - weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Aujourd\'hui à] LT', - nextDay: '[Demain à] LT', - nextWeek: 'dddd [à] LT', - lastDay: '[Hier à] LT', - lastWeek: 'dddd [dernier à] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'dans %s', - past : 'il y a %s', - s : 'quelques secondes', - m : 'une minute', - mm : '%d minutes', - h : 'une heure', - hh : '%d heures', - d : 'un jour', - dd : '%d jours', - M : 'un mois', - MM : '%d mois', - y : 'un an', - yy : '%d ans' - }, - ordinalParse: /\d{1,2}(er|)/, - ordinal : function (number) { - return number + (number === 1 ? 'er' : ''); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. +var fr = moment.defineLocale('fr', { + months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), + monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), + monthsParseExact : true, + weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), + weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), + weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Aujourd’hui à] LT', + nextDay : '[Demain à] LT', + nextWeek : 'dddd [à] LT', + lastDay : '[Hier à] LT', + lastWeek : 'dddd [dernier à] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'dans %s', + past : 'il y a %s', + s : 'quelques secondes', + m : 'une minute', + mm : '%d minutes', + h : 'une heure', + hh : '%d heures', + d : 'un jour', + dd : '%d jours', + M : 'un mois', + MM : '%d mois', + y : 'un an', + yy : '%d ans' + }, + dayOfMonthOrdinalParse: /\d{1,2}(er|)/, + ordinal : function (number, period) { + switch (period) { + // TODO: Return 'e' when day of month > 1. Move this case inside + // block for masculine words below. + // See https://github.com/moment/moment/issues/3375 + case 'D': + return number + (number === 1 ? 'er' : ''); + + // Words with masculine grammatical gender: mois, trimestre, jour + default: + case 'M': + case 'Q': + case 'DDD': + case 'd': + return number + (number === 1 ? 'er' : 'e'); + + // Words with feminine grammatical gender: semaine + case 'w': + case 'W': + return number + (number === 1 ? 're' : 'e'); } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return fr; +return fr; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/fy.js b/lib/javascripts/moment_locale/fy.js old mode 100755 new mode 100644 index 9d008ca7ad..2ed7d5b6e6 --- a/lib/javascripts/moment_locale/fy.js +++ b/lib/javascripts/moment_locale/fy.js @@ -1,73 +1,75 @@ //! moment.js locale configuration -//! locale : frisian (fy) +//! locale : Frisian [fy] //! author : Robin van der Vliet : https://github.com/robin0van0der0v ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var monthsShortWithDots = 'jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.'.split('_'), - monthsShortWithoutDots = 'jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'); +var monthsShortWithDots = 'jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.'.split('_'); +var monthsShortWithoutDots = 'jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'); - var fy = moment.defineLocale('fy', { - months : 'jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber'.split('_'), - monthsShort : function (m, format) { - if (/-MMM-/.test(format)) { - return monthsShortWithoutDots[m.month()]; - } else { - return monthsShortWithDots[m.month()]; - } - }, - monthsParseExact : true, - weekdays : 'snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon'.split('_'), - weekdaysShort : 'si._mo._ti._wo._to._fr._so.'.split('_'), - weekdaysMin : 'Si_Mo_Ti_Wo_To_Fr_So'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[hjoed om] LT', - nextDay: '[moarn om] LT', - nextWeek: 'dddd [om] LT', - lastDay: '[juster om] LT', - lastWeek: '[ôfrûne] dddd [om] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'oer %s', - past : '%s lyn', - s : 'in pear sekonden', - m : 'ien minút', - mm : '%d minuten', - h : 'ien oere', - hh : '%d oeren', - d : 'ien dei', - dd : '%d dagen', - M : 'ien moanne', - MM : '%d moannen', - y : 'ien jier', - yy : '%d jierren' - }, - ordinalParse: /\d{1,2}(ste|de)/, - ordinal : function (number) { - return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. +var fy = moment.defineLocale('fy', { + months : 'jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber'.split('_'), + monthsShort : function (m, format) { + if (!m) { + return monthsShortWithDots; + } else if (/-MMM-/.test(format)) { + return monthsShortWithoutDots[m.month()]; + } else { + return monthsShortWithDots[m.month()]; } - }); + }, + monthsParseExact : true, + weekdays : 'snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon'.split('_'), + weekdaysShort : 'si._mo._ti._wo._to._fr._so.'.split('_'), + weekdaysMin : 'Si_Mo_Ti_Wo_To_Fr_So'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD-MM-YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[hjoed om] LT', + nextDay: '[moarn om] LT', + nextWeek: 'dddd [om] LT', + lastDay: '[juster om] LT', + lastWeek: '[ôfrûne] dddd [om] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'oer %s', + past : '%s lyn', + s : 'in pear sekonden', + m : 'ien minút', + mm : '%d minuten', + h : 'ien oere', + hh : '%d oeren', + d : 'ien dei', + dd : '%d dagen', + M : 'ien moanne', + MM : '%d moannen', + y : 'ien jier', + yy : '%d jierren' + }, + dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, + ordinal : function (number) { + return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return fy; +return fy; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/gd.js b/lib/javascripts/moment_locale/gd.js old mode 100755 new mode 100644 index 578e5674b9..af9eec8479 --- a/lib/javascripts/moment_locale/gd.js +++ b/lib/javascripts/moment_locale/gd.js @@ -1,76 +1,76 @@ //! moment.js locale configuration -//! locale : great britain scottish gealic (gd) +//! locale : Scottish Gaelic [gd] //! author : Jon Ashdown : https://github.com/jonashdown ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var months = [ - 'Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean', 'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal', 'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd' - ]; +var months = [ + 'Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean', 'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal', 'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd' +]; - var monthsShort = ['Faoi', 'Gear', 'Màrt', 'Gibl', 'Cèit', 'Ògmh', 'Iuch', 'Lùn', 'Sult', 'Dàmh', 'Samh', 'Dùbh']; +var monthsShort = ['Faoi', 'Gear', 'Màrt', 'Gibl', 'Cèit', 'Ògmh', 'Iuch', 'Lùn', 'Sult', 'Dàmh', 'Samh', 'Dùbh']; - var weekdays = ['Didòmhnaich', 'Diluain', 'Dimàirt', 'Diciadain', 'Diardaoin', 'Dihaoine', 'Disathairne']; +var weekdays = ['Didòmhnaich', 'Diluain', 'Dimàirt', 'Diciadain', 'Diardaoin', 'Dihaoine', 'Disathairne']; - var weekdaysShort = ['Did', 'Dil', 'Dim', 'Dic', 'Dia', 'Dih', 'Dis']; +var weekdaysShort = ['Did', 'Dil', 'Dim', 'Dic', 'Dia', 'Dih', 'Dis']; - var weekdaysMin = ['Dò', 'Lu', 'Mà', 'Ci', 'Ar', 'Ha', 'Sa']; +var weekdaysMin = ['Dò', 'Lu', 'Mà', 'Ci', 'Ar', 'Ha', 'Sa']; - var gd = moment.defineLocale('gd', { - months : months, - monthsShort : monthsShort, - monthsParseExact : true, - weekdays : weekdays, - weekdaysShort : weekdaysShort, - weekdaysMin : weekdaysMin, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[An-diugh aig] LT', - nextDay : '[A-màireach aig] LT', - nextWeek : 'dddd [aig] LT', - lastDay : '[An-dè aig] LT', - lastWeek : 'dddd [seo chaidh] [aig] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'ann an %s', - past : 'bho chionn %s', - s : 'beagan diogan', - m : 'mionaid', - mm : '%d mionaidean', - h : 'uair', - hh : '%d uairean', - d : 'latha', - dd : '%d latha', - M : 'mìos', - MM : '%d mìosan', - y : 'bliadhna', - yy : '%d bliadhna' - }, - ordinalParse : /\d{1,2}(d|na|mh)/, - ordinal : function (number) { - var output = number === 1 ? 'd' : number % 10 === 2 ? 'na' : 'mh'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var gd = moment.defineLocale('gd', { + months : months, + monthsShort : monthsShort, + monthsParseExact : true, + weekdays : weekdays, + weekdaysShort : weekdaysShort, + weekdaysMin : weekdaysMin, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[An-diugh aig] LT', + nextDay : '[A-màireach aig] LT', + nextWeek : 'dddd [aig] LT', + lastDay : '[An-dè aig] LT', + lastWeek : 'dddd [seo chaidh] [aig] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'ann an %s', + past : 'bho chionn %s', + s : 'beagan diogan', + m : 'mionaid', + mm : '%d mionaidean', + h : 'uair', + hh : '%d uairean', + d : 'latha', + dd : '%d latha', + M : 'mìos', + MM : '%d mìosan', + y : 'bliadhna', + yy : '%d bliadhna' + }, + dayOfMonthOrdinalParse : /\d{1,2}(d|na|mh)/, + ordinal : function (number) { + var output = number === 1 ? 'd' : number % 10 === 2 ? 'na' : 'mh'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return gd; +return gd; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/gl.js b/lib/javascripts/moment_locale/gl.js old mode 100755 new mode 100644 index 0621d034cb..e894db7351 --- a/lib/javascripts/moment_locale/gl.js +++ b/lib/javascripts/moment_locale/gl.js @@ -1,77 +1,77 @@ //! moment.js locale configuration -//! locale : galician (gl) +//! locale : Galician [gl] //! author : Juan G. Hurtado : https://github.com/juanghurtado ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var gl = moment.defineLocale('gl', { - months : 'Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro'.split('_'), - monthsShort : 'Xan._Feb._Mar._Abr._Mai._Xuñ._Xul._Ago._Set._Out._Nov._Dec.'.split('_'), - monthsParseExact: true, - weekdays : 'Domingo_Luns_Martes_Mércores_Xoves_Venres_Sábado'.split('_'), - weekdaysShort : 'Dom._Lun._Mar._Mér._Xov._Ven._Sáb.'.split('_'), - weekdaysMin : 'Do_Lu_Ma_Mé_Xo_Ve_Sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd D MMMM YYYY H:mm' +var gl = moment.defineLocale('gl', { + months : 'xaneiro_febreiro_marzo_abril_maio_xuño_xullo_agosto_setembro_outubro_novembro_decembro'.split('_'), + monthsShort : 'xan._feb._mar._abr._mai._xuñ._xul._ago._set._out._nov._dec.'.split('_'), + monthsParseExact: true, + weekdays : 'domingo_luns_martes_mércores_xoves_venres_sábado'.split('_'), + weekdaysShort : 'dom._lun._mar._mér._xov._ven._sáb.'.split('_'), + weekdaysMin : 'do_lu_ma_mé_xo_ve_sá'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D [de] MMMM [de] YYYY', + LLL : 'D [de] MMMM [de] YYYY H:mm', + LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm' + }, + calendar : { + sameDay : function () { + return '[hoxe ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; }, - calendar : { - sameDay : function () { - return '[hoxe ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; - }, - nextDay : function () { - return '[mañá ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; - }, - nextWeek : function () { - return 'dddd [' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; - }, - lastDay : function () { - return '[onte ' + ((this.hours() !== 1) ? 'á' : 'a') + '] LT'; - }, - lastWeek : function () { - return '[o] dddd [pasado ' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; - }, - sameElse : 'L' + nextDay : function () { + return '[mañá ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; }, - relativeTime : { - future : function (str) { - if (str === 'uns segundos') { - return 'nuns segundos'; - } - return 'en ' + str; - }, - past : 'hai %s', - s : 'uns segundos', - m : 'un minuto', - mm : '%d minutos', - h : 'unha hora', - hh : '%d horas', - d : 'un día', - dd : '%d días', - M : 'un mes', - MM : '%d meses', - y : 'un ano', - yy : '%d anos' + nextWeek : function () { + return 'dddd [' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; }, - ordinalParse : /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + lastDay : function () { + return '[onte ' + ((this.hours() !== 1) ? 'á' : 'a') + '] LT'; + }, + lastWeek : function () { + return '[o] dddd [pasado ' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; + }, + sameElse : 'L' + }, + relativeTime : { + future : function (str) { + if (str.indexOf('un') === 0) { + return 'n' + str; + } + return 'en ' + str; + }, + past : 'hai %s', + s : 'uns segundos', + m : 'un minuto', + mm : '%d minutos', + h : 'unha hora', + hh : '%d horas', + d : 'un día', + dd : '%d días', + M : 'un mes', + MM : '%d meses', + y : 'un ano', + yy : '%d anos' + }, + dayOfMonthOrdinalParse : /\d{1,2}º/, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return gl; +return gl; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/gom-latn.js b/lib/javascripts/moment_locale/gom-latn.js new file mode 100644 index 0000000000..084b4b2260 --- /dev/null +++ b/lib/javascripts/moment_locale/gom-latn.js @@ -0,0 +1,122 @@ +//! moment.js locale configuration +//! locale : Konkani Latin script [gom-latn] +//! author : The Discoverer : https://github.com/WikiDiscoverer + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 's': ['thodde secondanim', 'thodde second'], + 'm': ['eka mintan', 'ek minute'], + 'mm': [number + ' mintanim', number + ' mintam'], + 'h': ['eka horan', 'ek hor'], + 'hh': [number + ' horanim', number + ' hor'], + 'd': ['eka disan', 'ek dis'], + 'dd': [number + ' disanim', number + ' dis'], + 'M': ['eka mhoinean', 'ek mhoino'], + 'MM': [number + ' mhoineanim', number + ' mhoine'], + 'y': ['eka vorsan', 'ek voros'], + 'yy': [number + ' vorsanim', number + ' vorsam'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; +} + +var gomLatn = moment.defineLocale('gom-latn', { + months : 'Janer_Febrer_Mars_Abril_Mai_Jun_Julai_Agost_Setembr_Otubr_Novembr_Dezembr'.split('_'), + monthsShort : 'Jan._Feb._Mars_Abr._Mai_Jun_Jul._Ago._Set._Otu._Nov._Dez.'.split('_'), + monthsParseExact : true, + weekdays : 'Aitar_Somar_Mongllar_Budvar_Brestar_Sukrar_Son\'var'.split('_'), + weekdaysShort : 'Ait._Som._Mon._Bud._Bre._Suk._Son.'.split('_'), + weekdaysMin : 'Ai_Sm_Mo_Bu_Br_Su_Sn'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'A h:mm [vazta]', + LTS : 'A h:mm:ss [vazta]', + L : 'DD-MM-YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY A h:mm [vazta]', + LLLL : 'dddd, MMMM[achea] Do, YYYY, A h:mm [vazta]', + llll: 'ddd, D MMM YYYY, A h:mm [vazta]' + }, + calendar : { + sameDay: '[Aiz] LT', + nextDay: '[Faleam] LT', + nextWeek: '[Ieta to] dddd[,] LT', + lastDay: '[Kal] LT', + lastWeek: '[Fatlo] dddd[,] LT', + sameElse: 'L' + }, + relativeTime : { + future : '%s', + past : '%s adim', + s : processRelativeTime, + m : processRelativeTime, + mm : processRelativeTime, + h : processRelativeTime, + hh : processRelativeTime, + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + dayOfMonthOrdinalParse : /\d{1,2}(er)/, + ordinal : function (number, period) { + switch (period) { + // the ordinal 'er' only applies to day of the month + case 'D': + return number + 'er'; + default: + case 'M': + case 'Q': + case 'DDD': + case 'd': + case 'w': + case 'W': + return number; + } + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + }, + meridiemParse: /rati|sokalli|donparam|sanje/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === 'rati') { + return hour < 4 ? hour : hour + 12; + } else if (meridiem === 'sokalli') { + return hour; + } else if (meridiem === 'donparam') { + return hour > 12 ? hour : hour + 12; + } else if (meridiem === 'sanje') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'rati'; + } else if (hour < 12) { + return 'sokalli'; + } else if (hour < 16) { + return 'donparam'; + } else if (hour < 20) { + return 'sanje'; + } else { + return 'rati'; + } + } +}); + +return gomLatn; + +}))); diff --git a/lib/javascripts/moment_locale/he.js b/lib/javascripts/moment_locale/he.js old mode 100755 new mode 100644 index ffc3e1fda4..e884381105 --- a/lib/javascripts/moment_locale/he.js +++ b/lib/javascripts/moment_locale/he.js @@ -1,5 +1,5 @@ //! moment.js locale configuration -//! locale : Hebrew (he) +//! locale : Hebrew [he] //! author : Tomer Cohen : https://github.com/tomer //! author : Moshe Simantov : https://github.com/DevelopmentIL //! author : Tal Ater : https://github.com/TalAter @@ -7,93 +7,93 @@ ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var he = moment.defineLocale('he', { - months : 'ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר'.split('_'), - monthsShort : 'ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳'.split('_'), - weekdays : 'ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת'.split('_'), - weekdaysShort : 'א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳'.split('_'), - weekdaysMin : 'א_ב_ג_ד_ה_ו_ש'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [ב]MMMM YYYY', - LLL : 'D [ב]MMMM YYYY HH:mm', - LLLL : 'dddd, D [ב]MMMM YYYY HH:mm', - l : 'D/M/YYYY', - ll : 'D MMM YYYY', - lll : 'D MMM YYYY HH:mm', - llll : 'ddd, D MMM YYYY HH:mm' - }, - calendar : { - sameDay : '[היום ב־]LT', - nextDay : '[מחר ב־]LT', - nextWeek : 'dddd [בשעה] LT', - lastDay : '[אתמול ב־]LT', - lastWeek : '[ביום] dddd [האחרון בשעה] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'בעוד %s', - past : 'לפני %s', - s : 'מספר שניות', - m : 'דקה', - mm : '%d דקות', - h : 'שעה', - hh : function (number) { - if (number === 2) { - return 'שעתיים'; - } - return number + ' שעות'; - }, - d : 'יום', - dd : function (number) { - if (number === 2) { - return 'יומיים'; - } - return number + ' ימים'; - }, - M : 'חודש', - MM : function (number) { - if (number === 2) { - return 'חודשיים'; - } - return number + ' חודשים'; - }, - y : 'שנה', - yy : function (number) { - if (number === 2) { - return 'שנתיים'; - } else if (number % 10 === 0 && number !== 10) { - return number + ' שנה'; - } - return number + ' שנים'; +var he = moment.defineLocale('he', { + months : 'ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר'.split('_'), + monthsShort : 'ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳'.split('_'), + weekdays : 'ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת'.split('_'), + weekdaysShort : 'א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳'.split('_'), + weekdaysMin : 'א_ב_ג_ד_ה_ו_ש'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D [ב]MMMM YYYY', + LLL : 'D [ב]MMMM YYYY HH:mm', + LLLL : 'dddd, D [ב]MMMM YYYY HH:mm', + l : 'D/M/YYYY', + ll : 'D MMM YYYY', + lll : 'D MMM YYYY HH:mm', + llll : 'ddd, D MMM YYYY HH:mm' + }, + calendar : { + sameDay : '[היום ב־]LT', + nextDay : '[מחר ב־]LT', + nextWeek : 'dddd [בשעה] LT', + lastDay : '[אתמול ב־]LT', + lastWeek : '[ביום] dddd [האחרון בשעה] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'בעוד %s', + past : 'לפני %s', + s : 'מספר שניות', + m : 'דקה', + mm : '%d דקות', + h : 'שעה', + hh : function (number) { + if (number === 2) { + return 'שעתיים'; } + return number + ' שעות'; }, - meridiemParse: /אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i, - isPM : function (input) { - return /^(אחה"צ|אחרי הצהריים|בערב)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 5) { - return 'לפנות בוקר'; - } else if (hour < 10) { - return 'בבוקר'; - } else if (hour < 12) { - return isLower ? 'לפנה"צ' : 'לפני הצהריים'; - } else if (hour < 18) { - return isLower ? 'אחה"צ' : 'אחרי הצהריים'; - } else { - return 'בערב'; + d : 'יום', + dd : function (number) { + if (number === 2) { + return 'יומיים'; } + return number + ' ימים'; + }, + M : 'חודש', + MM : function (number) { + if (number === 2) { + return 'חודשיים'; + } + return number + ' חודשים'; + }, + y : 'שנה', + yy : function (number) { + if (number === 2) { + return 'שנתיים'; + } else if (number % 10 === 0 && number !== 10) { + return number + ' שנה'; + } + return number + ' שנים'; } - }); + }, + meridiemParse: /אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i, + isPM : function (input) { + return /^(אחה"צ|אחרי הצהריים|בערב)$/.test(input); + }, + meridiem : function (hour, minute, isLower) { + if (hour < 5) { + return 'לפנות בוקר'; + } else if (hour < 10) { + return 'בבוקר'; + } else if (hour < 12) { + return isLower ? 'לפנה"צ' : 'לפני הצהריים'; + } else if (hour < 18) { + return isLower ? 'אחה"צ' : 'אחרי הצהריים'; + } else { + return 'בערב'; + } + } +}); - return he; +return he; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/hi.js b/lib/javascripts/moment_locale/hi.js old mode 100755 new mode 100644 index ebd272d11e..58afc6b1dd --- a/lib/javascripts/moment_locale/hi.js +++ b/lib/javascripts/moment_locale/hi.js @@ -1,124 +1,124 @@ //! moment.js locale configuration -//! locale : hindi (hi) +//! locale : Hindi [hi] //! author : Mayank Singhal : https://github.com/mayanksinghal ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '१', - '2': '२', - '3': '३', - '4': '४', - '5': '५', - '6': '६', - '7': '७', - '8': '८', - '9': '९', - '0': '०' +var symbolMap = { + '1': '१', + '2': '२', + '3': '३', + '4': '४', + '5': '५', + '6': '६', + '7': '७', + '8': '८', + '9': '९', + '0': '०' +}; +var numberMap = { + '१': '1', + '२': '2', + '३': '3', + '४': '4', + '५': '5', + '६': '6', + '७': '7', + '८': '8', + '९': '9', + '०': '0' +}; + +var hi = moment.defineLocale('hi', { + months : 'जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर'.split('_'), + monthsShort : 'जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.'.split('_'), + monthsParseExact: true, + weekdays : 'रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'), + weekdaysShort : 'रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि'.split('_'), + weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'), + longDateFormat : { + LT : 'A h:mm बजे', + LTS : 'A h:mm:ss बजे', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm बजे', + LLLL : 'dddd, D MMMM YYYY, A h:mm बजे' }, - numberMap = { - '१': '1', - '२': '2', - '३': '3', - '४': '4', - '५': '5', - '६': '6', - '७': '7', - '८': '8', - '९': '9', - '०': '0' - }; - - var hi = moment.defineLocale('hi', { - months : 'जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर'.split('_'), - monthsShort : 'जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.'.split('_'), - monthsParseExact: true, - weekdays : 'रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'), - weekdaysShort : 'रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि'.split('_'), - weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'), - longDateFormat : { - LT : 'A h:mm बजे', - LTS : 'A h:mm:ss बजे', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm बजे', - LLLL : 'dddd, D MMMM YYYY, A h:mm बजे' - }, - calendar : { - sameDay : '[आज] LT', - nextDay : '[कल] LT', - nextWeek : 'dddd, LT', - lastDay : '[कल] LT', - lastWeek : '[पिछले] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s में', - past : '%s पहले', - s : 'कुछ ही क्षण', - m : 'एक मिनट', - mm : '%d मिनट', - h : 'एक घंटा', - hh : '%d घंटे', - d : 'एक दिन', - dd : '%d दिन', - M : 'एक महीने', - MM : '%d महीने', - y : 'एक वर्ष', - yy : '%d वर्ष' - }, - preparse: function (string) { - return string.replace(/[१२३४५६७८९०]/g, function (match) { - return numberMap[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }); - }, - // Hindi notation for meridiems are quite fuzzy in practice. While there exists - // a rigid notion of a 'Pahar' it is not used as rigidly in modern Hindi. - meridiemParse: /रात|सुबह|दोपहर|शाम/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'रात') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'सुबह') { - return hour; - } else if (meridiem === 'दोपहर') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'शाम') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'रात'; - } else if (hour < 10) { - return 'सुबह'; - } else if (hour < 17) { - return 'दोपहर'; - } else if (hour < 20) { - return 'शाम'; - } else { - return 'रात'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. + calendar : { + sameDay : '[आज] LT', + nextDay : '[कल] LT', + nextWeek : 'dddd, LT', + lastDay : '[कल] LT', + lastWeek : '[पिछले] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s में', + past : '%s पहले', + s : 'कुछ ही क्षण', + m : 'एक मिनट', + mm : '%d मिनट', + h : 'एक घंटा', + hh : '%d घंटे', + d : 'एक दिन', + dd : '%d दिन', + M : 'एक महीने', + MM : '%d महीने', + y : 'एक वर्ष', + yy : '%d वर्ष' + }, + preparse: function (string) { + return string.replace(/[१२३४५६७८९०]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + // Hindi notation for meridiems are quite fuzzy in practice. While there exists + // a rigid notion of a 'Pahar' it is not used as rigidly in modern Hindi. + meridiemParse: /रात|सुबह|दोपहर|शाम/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if (meridiem === 'रात') { + return hour < 4 ? hour : hour + 12; + } else if (meridiem === 'सुबह') { + return hour; + } else if (meridiem === 'दोपहर') { + return hour >= 10 ? hour : hour + 12; + } else if (meridiem === 'शाम') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'रात'; + } else if (hour < 10) { + return 'सुबह'; + } else if (hour < 17) { + return 'दोपहर'; + } else if (hour < 20) { + return 'शाम'; + } else { + return 'रात'; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); - return hi; +return hi; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/hr.js b/lib/javascripts/moment_locale/hr.js old mode 100755 new mode 100644 index a96f3a5a15..5caeec8028 --- a/lib/javascripts/moment_locale/hr.js +++ b/lib/javascripts/moment_locale/hr.js @@ -1,18 +1,18 @@ //! moment.js locale configuration -//! locale : hrvatski (hr) +//! locale : Croatian [hr] //! author : Bojan Marković : https://github.com/bmarkovic ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function translate(number, withoutSuffix, key) { - var result = number + ' '; - switch (key) { +function translate(number, withoutSuffix, key) { + var result = number + ' '; + switch (key) { case 'm': return withoutSuffix ? 'jedna minuta' : 'jedne minute'; case 'mm': @@ -60,33 +60,33 @@ result += 'godina'; } return result; - } } +} - var hr = moment.defineLocale('hr', { - months : { - format: 'siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca'.split('_'), - standalone: 'siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac'.split('_') - }, - monthsShort : 'sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.'.split('_'), - monthsParseExact: true, - weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), - weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), - weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD. MM. YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[danas u] LT', - nextDay : '[sutra u] LT', - nextWeek : function () { - switch (this.day()) { +var hr = moment.defineLocale('hr', { + months : { + format: 'siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca'.split('_'), + standalone: 'siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac'.split('_') + }, + monthsShort : 'sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.'.split('_'), + monthsParseExact: true, + weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), + weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), + weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY H:mm', + LLLL : 'dddd, D. MMMM YYYY H:mm' + }, + calendar : { + sameDay : '[danas u] LT', + nextDay : '[sutra u] LT', + nextWeek : function () { + switch (this.day()) { case 0: return '[u] [nedjelju] [u] LT'; case 3: @@ -98,11 +98,11 @@ case 4: case 5: return '[u] dddd [u] LT'; - } - }, - lastDay : '[jučer u] LT', - lastWeek : function () { - switch (this.day()) { + } + }, + lastDay : '[jučer u] LT', + lastWeek : function () { + switch (this.day()) { case 0: case 3: return '[prošlu] dddd [u] LT'; @@ -113,33 +113,33 @@ case 4: case 5: return '[prošli] dddd [u] LT'; - } - }, - sameElse : 'L' + } }, - relativeTime : { - future : 'za %s', - past : 'prije %s', - s : 'par sekundi', - m : translate, - mm : translate, - h : translate, - hh : translate, - d : 'dan', - dd : translate, - M : 'mjesec', - MM : translate, - y : 'godinu', - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + sameElse : 'L' + }, + relativeTime : { + future : 'za %s', + past : 'prije %s', + s : 'par sekundi', + m : translate, + mm : translate, + h : translate, + hh : translate, + d : 'dan', + dd : translate, + M : 'mjesec', + MM : translate, + y : 'godinu', + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return hr; +return hr; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/hu.js b/lib/javascripts/moment_locale/hu.js old mode 100755 new mode 100644 index 2708672742..e2158741eb --- a/lib/javascripts/moment_locale/hu.js +++ b/lib/javascripts/moment_locale/hu.js @@ -1,20 +1,20 @@ //! moment.js locale configuration -//! locale : hungarian (hu) +//! locale : Hungarian [hu] //! author : Adam Brunner : https://github.com/adambrunner ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var weekEndings = 'vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton'.split(' '); - function translate(number, withoutSuffix, key, isFuture) { - var num = number, - suffix; - switch (key) { +var weekEndings = 'vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton'.split(' '); +function translate(number, withoutSuffix, key, isFuture) { + var num = number, + suffix; + switch (key) { case 's': return (isFuture || withoutSuffix) ? 'néhány másodperc' : 'néhány másodperce'; case 'm': @@ -37,73 +37,73 @@ return 'egy' + (isFuture || withoutSuffix ? ' év' : ' éve'); case 'yy': return num + (isFuture || withoutSuffix ? ' év' : ' éve'); - } - return ''; - } - function week(isFuture) { - return (isFuture ? '' : '[múlt] ') + '[' + weekEndings[this.day()] + '] LT[-kor]'; } + return ''; +} +function week(isFuture) { + return (isFuture ? '' : '[múlt] ') + '[' + weekEndings[this.day()] + '] LT[-kor]'; +} - var hu = moment.defineLocale('hu', { - months : 'január_február_március_április_május_június_július_augusztus_szeptember_október_november_december'.split('_'), - monthsShort : 'jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec'.split('_'), - weekdays : 'vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat'.split('_'), - weekdaysShort : 'vas_hét_kedd_sze_csüt_pén_szo'.split('_'), - weekdaysMin : 'v_h_k_sze_cs_p_szo'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'YYYY.MM.DD.', - LL : 'YYYY. MMMM D.', - LLL : 'YYYY. MMMM D. H:mm', - LLLL : 'YYYY. MMMM D., dddd H:mm' - }, - meridiemParse: /de|du/i, - isPM: function (input) { - return input.charAt(1).toLowerCase() === 'u'; - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 12) { - return isLower === true ? 'de' : 'DE'; - } else { - return isLower === true ? 'du' : 'DU'; - } - }, - calendar : { - sameDay : '[ma] LT[-kor]', - nextDay : '[holnap] LT[-kor]', - nextWeek : function () { - return week.call(this, true); - }, - lastDay : '[tegnap] LT[-kor]', - lastWeek : function () { - return week.call(this, false); - }, - sameElse : 'L' - }, - relativeTime : { - future : '%s múlva', - past : '%s', - s : translate, - m : translate, - mm : translate, - h : translate, - hh : translate, - d : translate, - dd : translate, - M : translate, - MM : translate, - y : translate, - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var hu = moment.defineLocale('hu', { + months : 'január_február_március_április_május_június_július_augusztus_szeptember_október_november_december'.split('_'), + monthsShort : 'jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec'.split('_'), + weekdays : 'vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat'.split('_'), + weekdaysShort : 'vas_hét_kedd_sze_csüt_pén_szo'.split('_'), + weekdaysMin : 'v_h_k_sze_cs_p_szo'.split('_'), + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'YYYY.MM.DD.', + LL : 'YYYY. MMMM D.', + LLL : 'YYYY. MMMM D. H:mm', + LLLL : 'YYYY. MMMM D., dddd H:mm' + }, + meridiemParse: /de|du/i, + isPM: function (input) { + return input.charAt(1).toLowerCase() === 'u'; + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 12) { + return isLower === true ? 'de' : 'DE'; + } else { + return isLower === true ? 'du' : 'DU'; } - }); + }, + calendar : { + sameDay : '[ma] LT[-kor]', + nextDay : '[holnap] LT[-kor]', + nextWeek : function () { + return week.call(this, true); + }, + lastDay : '[tegnap] LT[-kor]', + lastWeek : function () { + return week.call(this, false); + }, + sameElse : 'L' + }, + relativeTime : { + future : '%s múlva', + past : '%s', + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return hu; +return hu; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/hy-am.js b/lib/javascripts/moment_locale/hy-am.js old mode 100755 new mode 100644 index 7350bfbe45..0b76845883 --- a/lib/javascripts/moment_locale/hy-am.js +++ b/lib/javascripts/moment_locale/hy-am.js @@ -1,77 +1,77 @@ //! moment.js locale configuration -//! locale : Armenian (hy-am) +//! locale : Armenian [hy-am] //! author : Armendarabyan : https://github.com/armendarabyan ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var hy_am = moment.defineLocale('hy-am', { - months : { - format: 'հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի'.split('_'), - standalone: 'հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր'.split('_') +var hyAm = moment.defineLocale('hy-am', { + months : { + format: 'հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի'.split('_'), + standalone: 'հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր'.split('_') + }, + monthsShort : 'հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ'.split('_'), + weekdays : 'կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ'.split('_'), + weekdaysShort : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'), + weekdaysMin : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY թ.', + LLL : 'D MMMM YYYY թ., HH:mm', + LLLL : 'dddd, D MMMM YYYY թ., HH:mm' + }, + calendar : { + sameDay: '[այսօր] LT', + nextDay: '[վաղը] LT', + lastDay: '[երեկ] LT', + nextWeek: function () { + return 'dddd [օրը ժամը] LT'; }, - monthsShort : 'հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ'.split('_'), - weekdays : 'կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ'.split('_'), - weekdaysShort : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'), - weekdaysMin : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY թ.', - LLL : 'D MMMM YYYY թ., HH:mm', - LLLL : 'dddd, D MMMM YYYY թ., HH:mm' + lastWeek: function () { + return '[անցած] dddd [օրը ժամը] LT'; }, - calendar : { - sameDay: '[այսօր] LT', - nextDay: '[վաղը] LT', - lastDay: '[երեկ] LT', - nextWeek: function () { - return 'dddd [օրը ժամը] LT'; - }, - lastWeek: function () { - return '[անցած] dddd [օրը ժամը] LT'; - }, - sameElse: 'L' - }, - relativeTime : { - future : '%s հետո', - past : '%s առաջ', - s : 'մի քանի վայրկյան', - m : 'րոպե', - mm : '%d րոպե', - h : 'ժամ', - hh : '%d ժամ', - d : 'օր', - dd : '%d օր', - M : 'ամիս', - MM : '%d ամիս', - y : 'տարի', - yy : '%d տարի' - }, - meridiemParse: /գիշերվա|առավոտվա|ցերեկվա|երեկոյան/, - isPM: function (input) { - return /^(ցերեկվա|երեկոյան)$/.test(input); - }, - meridiem : function (hour) { - if (hour < 4) { - return 'գիշերվա'; - } else if (hour < 12) { - return 'առավոտվա'; - } else if (hour < 17) { - return 'ցերեկվա'; - } else { - return 'երեկոյան'; - } - }, - ordinalParse: /\d{1,2}|\d{1,2}-(ին|րդ)/, - ordinal: function (number, period) { - switch (period) { + sameElse: 'L' + }, + relativeTime : { + future : '%s հետո', + past : '%s առաջ', + s : 'մի քանի վայրկյան', + m : 'րոպե', + mm : '%d րոպե', + h : 'ժամ', + hh : '%d ժամ', + d : 'օր', + dd : '%d օր', + M : 'ամիս', + MM : '%d ամիս', + y : 'տարի', + yy : '%d տարի' + }, + meridiemParse: /գիշերվա|առավոտվա|ցերեկվա|երեկոյան/, + isPM: function (input) { + return /^(ցերեկվա|երեկոյան)$/.test(input); + }, + meridiem : function (hour) { + if (hour < 4) { + return 'գիշերվա'; + } else if (hour < 12) { + return 'առավոտվա'; + } else if (hour < 17) { + return 'ցերեկվա'; + } else { + return 'երեկոյան'; + } + }, + dayOfMonthOrdinalParse: /\d{1,2}|\d{1,2}-(ին|րդ)/, + ordinal: function (number, period) { + switch (period) { case 'DDD': case 'w': case 'W': @@ -82,14 +82,14 @@ return number + '-րդ'; default: return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return hy_am; +return hyAm; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/id.js b/lib/javascripts/moment_locale/id.js old mode 100755 new mode 100644 index 09461a5d5a..a1cfb18345 --- a/lib/javascripts/moment_locale/id.js +++ b/lib/javascripts/moment_locale/id.js @@ -1,83 +1,83 @@ //! moment.js locale configuration -//! locale : Bahasa Indonesia (id) +//! locale : Indonesian [id] //! author : Mohammad Satrio Utomo : https://github.com/tyok //! reference: http://id.wikisource.org/wiki/Pedoman_Umum_Ejaan_Bahasa_Indonesia_yang_Disempurnakan ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var id = moment.defineLocale('id', { - months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des'.split('_'), - weekdays : 'Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu'.split('_'), - weekdaysShort : 'Min_Sen_Sel_Rab_Kam_Jum_Sab'.split('_'), - weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sb'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /pagi|siang|sore|malam/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'pagi') { - return hour; - } else if (meridiem === 'siang') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'sore' || meridiem === 'malam') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'pagi'; - } else if (hours < 15) { - return 'siang'; - } else if (hours < 19) { - return 'sore'; - } else { - return 'malam'; - } - }, - calendar : { - sameDay : '[Hari ini pukul] LT', - nextDay : '[Besok pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kemarin pukul] LT', - lastWeek : 'dddd [lalu pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dalam %s', - past : '%s yang lalu', - s : 'beberapa detik', - m : 'semenit', - mm : '%d menit', - h : 'sejam', - hh : '%d jam', - d : 'sehari', - dd : '%d hari', - M : 'sebulan', - MM : '%d bulan', - y : 'setahun', - yy : '%d tahun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var id = moment.defineLocale('id', { + months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des'.split('_'), + weekdays : 'Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu'.split('_'), + weekdaysShort : 'Min_Sen_Sel_Rab_Kam_Jum_Sab'.split('_'), + weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sb'.split('_'), + longDateFormat : { + LT : 'HH.mm', + LTS : 'HH.mm.ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY [pukul] HH.mm', + LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' + }, + meridiemParse: /pagi|siang|sore|malam/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if (meridiem === 'pagi') { + return hour; + } else if (meridiem === 'siang') { + return hour >= 11 ? hour : hour + 12; + } else if (meridiem === 'sore' || meridiem === 'malam') { + return hour + 12; + } + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 11) { + return 'pagi'; + } else if (hours < 15) { + return 'siang'; + } else if (hours < 19) { + return 'sore'; + } else { + return 'malam'; + } + }, + calendar : { + sameDay : '[Hari ini pukul] LT', + nextDay : '[Besok pukul] LT', + nextWeek : 'dddd [pukul] LT', + lastDay : '[Kemarin pukul] LT', + lastWeek : 'dddd [lalu pukul] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'dalam %s', + past : '%s yang lalu', + s : 'beberapa detik', + m : 'semenit', + mm : '%d menit', + h : 'sejam', + hh : '%d jam', + d : 'sehari', + dd : '%d hari', + M : 'sebulan', + MM : '%d bulan', + y : 'setahun', + yy : '%d tahun' + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return id; +return id; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/is.js b/lib/javascripts/moment_locale/is.js old mode 100755 new mode 100644 index b6f63ca7fb..541b270e5f --- a/lib/javascripts/moment_locale/is.js +++ b/lib/javascripts/moment_locale/is.js @@ -1,26 +1,26 @@ //! moment.js locale configuration -//! locale : icelandic (is) +//! locale : Icelandic [is] //! author : Hinrik Örn Sigurðsson : https://github.com/hinrik ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function plural(n) { - if (n % 100 === 11) { - return true; - } else if (n % 10 === 1) { - return false; - } +function plural(n) { + if (n % 100 === 11) { return true; + } else if (n % 10 === 1) { + return false; } - function translate(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { + return true; +} +function translate(number, withoutSuffix, key, isFuture) { + var result = number + ' '; + switch (key) { case 's': return withoutSuffix || isFuture ? 'nokkrar sekúndur' : 'nokkrum sekúndum'; case 'm': @@ -74,54 +74,54 @@ return result + (withoutSuffix || isFuture ? 'ár' : 'árum'); } return result + (withoutSuffix || isFuture ? 'ár' : 'ári'); - } } +} - var is = moment.defineLocale('is', { - months : 'janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember'.split('_'), - monthsShort : 'jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des'.split('_'), - weekdays : 'sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur'.split('_'), - weekdaysShort : 'sun_mán_þri_mið_fim_fös_lau'.split('_'), - weekdaysMin : 'Su_Má_Þr_Mi_Fi_Fö_La'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY [kl.] H:mm', - LLLL : 'dddd, D. MMMM YYYY [kl.] H:mm' - }, - calendar : { - sameDay : '[í dag kl.] LT', - nextDay : '[á morgun kl.] LT', - nextWeek : 'dddd [kl.] LT', - lastDay : '[í gær kl.] LT', - lastWeek : '[síðasta] dddd [kl.] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'eftir %s', - past : 'fyrir %s síðan', - s : translate, - m : translate, - mm : translate, - h : 'klukkustund', - hh : translate, - d : translate, - dd : translate, - M : translate, - MM : translate, - y : translate, - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var is = moment.defineLocale('is', { + months : 'janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember'.split('_'), + monthsShort : 'jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des'.split('_'), + weekdays : 'sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur'.split('_'), + weekdaysShort : 'sun_mán_þri_mið_fim_fös_lau'.split('_'), + weekdaysMin : 'Su_Má_Þr_Mi_Fi_Fö_La'.split('_'), + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY [kl.] H:mm', + LLLL : 'dddd, D. MMMM YYYY [kl.] H:mm' + }, + calendar : { + sameDay : '[í dag kl.] LT', + nextDay : '[á morgun kl.] LT', + nextWeek : 'dddd [kl.] LT', + lastDay : '[í gær kl.] LT', + lastWeek : '[síðasta] dddd [kl.] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'eftir %s', + past : 'fyrir %s síðan', + s : translate, + m : translate, + mm : translate, + h : 'klukkustund', + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return is; +return is; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/it.js b/lib/javascripts/moment_locale/it.js old mode 100755 new mode 100644 index e8b2c950c7..f3079cb465 --- a/lib/javascripts/moment_locale/it.js +++ b/lib/javascripts/moment_locale/it.js @@ -1,70 +1,70 @@ //! moment.js locale configuration -//! locale : italian (it) +//! locale : Italian [it] //! author : Lorenzo : https://github.com/aliem //! author: Mattia Larentis: https://github.com/nostalgiaz ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var it = moment.defineLocale('it', { - months : 'gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre'.split('_'), - monthsShort : 'gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic'.split('_'), - weekdays : 'Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato'.split('_'), - weekdaysShort : 'Dom_Lun_Mar_Mer_Gio_Ven_Sab'.split('_'), - weekdaysMin : 'Do_Lu_Ma_Me_Gi_Ve_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' +var it = moment.defineLocale('it', { + months : 'gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre'.split('_'), + monthsShort : 'gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic'.split('_'), + weekdays : 'domenica_lunedì_martedì_mercoledì_giovedì_venerdì_sabato'.split('_'), + weekdaysShort : 'dom_lun_mar_mer_gio_ven_sab'.split('_'), + weekdaysMin : 'do_lu_ma_me_gi_ve_sa'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[Oggi alle] LT', + nextDay: '[Domani alle] LT', + nextWeek: 'dddd [alle] LT', + lastDay: '[Ieri alle] LT', + lastWeek: function () { + switch (this.day()) { + case 0: + return '[la scorsa] dddd [alle] LT'; + default: + return '[lo scorso] dddd [alle] LT'; + } }, - calendar : { - sameDay: '[Oggi alle] LT', - nextDay: '[Domani alle] LT', - nextWeek: 'dddd [alle] LT', - lastDay: '[Ieri alle] LT', - lastWeek: function () { - switch (this.day()) { - case 0: - return '[la scorsa] dddd [alle] LT'; - default: - return '[lo scorso] dddd [alle] LT'; - } - }, - sameElse: 'L' + sameElse: 'L' + }, + relativeTime : { + future : function (s) { + return ((/^[0-9].+$/).test(s) ? 'tra' : 'in') + ' ' + s; }, - relativeTime : { - future : function (s) { - return ((/^[0-9].+$/).test(s) ? 'tra' : 'in') + ' ' + s; - }, - past : '%s fa', - s : 'alcuni secondi', - m : 'un minuto', - mm : '%d minuti', - h : 'un\'ora', - hh : '%d ore', - d : 'un giorno', - dd : '%d giorni', - M : 'un mese', - MM : '%d mesi', - y : 'un anno', - yy : '%d anni' - }, - ordinalParse : /\d{1,2}º/, - ordinal: '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); + past : '%s fa', + s : 'alcuni secondi', + m : 'un minuto', + mm : '%d minuti', + h : 'un\'ora', + hh : '%d ore', + d : 'un giorno', + dd : '%d giorni', + M : 'un mese', + MM : '%d mesi', + y : 'un anno', + yy : '%d anni' + }, + dayOfMonthOrdinalParse : /\d{1,2}º/, + ordinal: '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return it; +return it; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ja.js b/lib/javascripts/moment_locale/ja.js old mode 100755 new mode 100644 index 147543930e..b2e655395e --- a/lib/javascripts/moment_locale/ja.js +++ b/lib/javascripts/moment_locale/ja.js @@ -1,76 +1,80 @@ //! moment.js locale configuration -//! locale : japanese (ja) +//! locale : Japanese [ja] //! author : LI Long : https://github.com/baryon ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ja = moment.defineLocale('ja', { - months : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - weekdays : '日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日'.split('_'), - weekdaysShort : '日_月_火_水_木_金_土'.split('_'), - weekdaysMin : '日_月_火_水_木_金_土'.split('_'), - longDateFormat : { - LT : 'Ah時m分', - LTS : 'Ah時m分s秒', - L : 'YYYY/MM/DD', - LL : 'YYYY年M月D日', - LLL : 'YYYY年M月D日Ah時m分', - LLLL : 'YYYY年M月D日Ah時m分 dddd' - }, - meridiemParse: /午前|午後/i, - isPM : function (input) { - return input === '午後'; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return '午前'; - } else { - return '午後'; - } - }, - calendar : { - sameDay : '[今日] LT', - nextDay : '[明日] LT', - nextWeek : '[来週]dddd LT', - lastDay : '[昨日] LT', - lastWeek : '[前週]dddd LT', - sameElse : 'L' - }, - ordinalParse : /\d{1,2}日/, - ordinal : function (number, period) { - switch (period) { +var ja = moment.defineLocale('ja', { + months : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), + monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), + weekdays : '日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日'.split('_'), + weekdaysShort : '日_月_火_水_木_金_土'.split('_'), + weekdaysMin : '日_月_火_水_木_金_土'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY/MM/DD', + LL : 'YYYY年M月D日', + LLL : 'YYYY年M月D日 HH:mm', + LLLL : 'YYYY年M月D日 HH:mm dddd', + l : 'YYYY/MM/DD', + ll : 'YYYY年M月D日', + lll : 'YYYY年M月D日 HH:mm', + llll : 'YYYY年M月D日 HH:mm dddd' + }, + meridiemParse: /午前|午後/i, + isPM : function (input) { + return input === '午後'; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return '午前'; + } else { + return '午後'; + } + }, + calendar : { + sameDay : '[今日] LT', + nextDay : '[明日] LT', + nextWeek : '[来週]dddd LT', + lastDay : '[昨日] LT', + lastWeek : '[前週]dddd LT', + sameElse : 'L' + }, + dayOfMonthOrdinalParse : /\d{1,2}日/, + ordinal : function (number, period) { + switch (period) { case 'd': case 'D': case 'DDD': return number + '日'; default: return number; - } - }, - relativeTime : { - future : '%s後', - past : '%s前', - s : '数秒', - m : '1分', - mm : '%d分', - h : '1時間', - hh : '%d時間', - d : '1日', - dd : '%d日', - M : '1ヶ月', - MM : '%dヶ月', - y : '1年', - yy : '%d年' } - }); + }, + relativeTime : { + future : '%s後', + past : '%s前', + s : '数秒', + m : '1分', + mm : '%d分', + h : '1時間', + hh : '%d時間', + d : '1日', + dd : '%d日', + M : '1ヶ月', + MM : '%dヶ月', + y : '1年', + yy : '%d年' + } +}); - return ja; +return ja; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/jv.js b/lib/javascripts/moment_locale/jv.js old mode 100755 new mode 100644 index d3b85a4c06..5b939e0fcb --- a/lib/javascripts/moment_locale/jv.js +++ b/lib/javascripts/moment_locale/jv.js @@ -1,83 +1,83 @@ //! moment.js locale configuration -//! locale : Boso Jowo (jv) +//! locale : Javanese [jv] //! author : Rony Lantip : https://github.com/lantip //! reference: http://jv.wikipedia.org/wiki/Basa_Jawa ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var jv = moment.defineLocale('jv', { - months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des'.split('_'), - weekdays : 'Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu'.split('_'), - weekdaysShort : 'Min_Sen_Sel_Reb_Kem_Jem_Sep'.split('_'), - weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sp'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /enjing|siyang|sonten|ndalu/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'enjing') { - return hour; - } else if (meridiem === 'siyang') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'sonten' || meridiem === 'ndalu') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'enjing'; - } else if (hours < 15) { - return 'siyang'; - } else if (hours < 19) { - return 'sonten'; - } else { - return 'ndalu'; - } - }, - calendar : { - sameDay : '[Dinten puniko pukul] LT', - nextDay : '[Mbenjang pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kala wingi pukul] LT', - lastWeek : 'dddd [kepengker pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'wonten ing %s', - past : '%s ingkang kepengker', - s : 'sawetawis detik', - m : 'setunggal menit', - mm : '%d menit', - h : 'setunggal jam', - hh : '%d jam', - d : 'sedinten', - dd : '%d dinten', - M : 'sewulan', - MM : '%d wulan', - y : 'setaun', - yy : '%d taun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var jv = moment.defineLocale('jv', { + months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember'.split('_'), + monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des'.split('_'), + weekdays : 'Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu'.split('_'), + weekdaysShort : 'Min_Sen_Sel_Reb_Kem_Jem_Sep'.split('_'), + weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sp'.split('_'), + longDateFormat : { + LT : 'HH.mm', + LTS : 'HH.mm.ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY [pukul] HH.mm', + LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' + }, + meridiemParse: /enjing|siyang|sonten|ndalu/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if (meridiem === 'enjing') { + return hour; + } else if (meridiem === 'siyang') { + return hour >= 11 ? hour : hour + 12; + } else if (meridiem === 'sonten' || meridiem === 'ndalu') { + return hour + 12; + } + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 11) { + return 'enjing'; + } else if (hours < 15) { + return 'siyang'; + } else if (hours < 19) { + return 'sonten'; + } else { + return 'ndalu'; + } + }, + calendar : { + sameDay : '[Dinten puniko pukul] LT', + nextDay : '[Mbenjang pukul] LT', + nextWeek : 'dddd [pukul] LT', + lastDay : '[Kala wingi pukul] LT', + lastWeek : 'dddd [kepengker pukul] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'wonten ing %s', + past : '%s ingkang kepengker', + s : 'sawetawis detik', + m : 'setunggal menit', + mm : '%d menit', + h : 'setunggal jam', + hh : '%d jam', + d : 'sedinten', + dd : '%d dinten', + M : 'sewulan', + MM : '%d wulan', + y : 'setaun', + yy : '%d taun' + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return jv; +return jv; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ka.js b/lib/javascripts/moment_locale/ka.js old mode 100755 new mode 100644 index f052c3ac33..2a95638a8f --- a/lib/javascripts/moment_locale/ka.js +++ b/lib/javascripts/moment_locale/ka.js @@ -1,89 +1,89 @@ //! moment.js locale configuration -//! locale : Georgian (ka) +//! locale : Georgian [ka] //! author : Irakli Janiashvili : https://github.com/irakli-janiashvili ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ka = moment.defineLocale('ka', { - months : { - standalone: 'იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი'.split('_'), - format: 'იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს'.split('_') +var ka = moment.defineLocale('ka', { + months : { + standalone: 'იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი'.split('_'), + format: 'იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს'.split('_') + }, + monthsShort : 'იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ'.split('_'), + weekdays : { + standalone: 'კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი'.split('_'), + format: 'კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს'.split('_'), + isFormat: /(წინა|შემდეგ)/ + }, + weekdaysShort : 'კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ'.split('_'), + weekdaysMin : 'კვ_ორ_სა_ოთ_ხუ_პა_შა'.split('_'), + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY h:mm A', + LLLL : 'dddd, D MMMM YYYY h:mm A' + }, + calendar : { + sameDay : '[დღეს] LT[-ზე]', + nextDay : '[ხვალ] LT[-ზე]', + lastDay : '[გუშინ] LT[-ზე]', + nextWeek : '[შემდეგ] dddd LT[-ზე]', + lastWeek : '[წინა] dddd LT-ზე', + sameElse : 'L' + }, + relativeTime : { + future : function (s) { + return (/(წამი|წუთი|საათი|წელი)/).test(s) ? + s.replace(/ი$/, 'ში') : + s + 'ში'; }, - monthsShort : 'იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ'.split('_'), - weekdays : { - standalone: 'კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი'.split('_'), - format: 'კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს'.split('_'), - isFormat: /(წინა|შემდეგ)/ - }, - weekdaysShort : 'კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ'.split('_'), - weekdaysMin : 'კვ_ორ_სა_ოთ_ხუ_პა_შა'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[დღეს] LT[-ზე]', - nextDay : '[ხვალ] LT[-ზე]', - lastDay : '[გუშინ] LT[-ზე]', - nextWeek : '[შემდეგ] dddd LT[-ზე]', - lastWeek : '[წინა] dddd LT-ზე', - sameElse : 'L' - }, - relativeTime : { - future : function (s) { - return (/(წამი|წუთი|საათი|წელი)/).test(s) ? - s.replace(/ი$/, 'ში') : - s + 'ში'; - }, - past : function (s) { - if ((/(წამი|წუთი|საათი|დღე|თვე)/).test(s)) { - return s.replace(/(ი|ე)$/, 'ის წინ'); - } - if ((/წელი/).test(s)) { - return s.replace(/წელი$/, 'წლის წინ'); - } - }, - s : 'რამდენიმე წამი', - m : 'წუთი', - mm : '%d წუთი', - h : 'საათი', - hh : '%d საათი', - d : 'დღე', - dd : '%d დღე', - M : 'თვე', - MM : '%d თვე', - y : 'წელი', - yy : '%d წელი' - }, - ordinalParse: /0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/, - ordinal : function (number) { - if (number === 0) { - return number; + past : function (s) { + if ((/(წამი|წუთი|საათი|დღე|თვე)/).test(s)) { + return s.replace(/(ი|ე)$/, 'ის უკან'); } - if (number === 1) { - return number + '-ლი'; + if ((/წელი/).test(s)) { + return s.replace(/წელი$/, 'წლის უკან'); } - if ((number < 20) || (number <= 100 && (number % 20 === 0)) || (number % 100 === 0)) { - return 'მე-' + number; - } - return number + '-ე'; }, - week : { - dow : 1, - doy : 7 + s : 'რამდენიმე წამი', + m : 'წუთი', + mm : '%d წუთი', + h : 'საათი', + hh : '%d საათი', + d : 'დღე', + dd : '%d დღე', + M : 'თვე', + MM : '%d თვე', + y : 'წელი', + yy : '%d წელი' + }, + dayOfMonthOrdinalParse: /0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/, + ordinal : function (number) { + if (number === 0) { + return number; } - }); + if (number === 1) { + return number + '-ლი'; + } + if ((number < 20) || (number <= 100 && (number % 20 === 0)) || (number % 100 === 0)) { + return 'მე-' + number; + } + return number + '-ე'; + }, + week : { + dow : 1, + doy : 7 + } +}); - return ka; +return ka; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/kk.js b/lib/javascripts/moment_locale/kk.js old mode 100755 new mode 100644 index 9c00588239..777a5294de --- a/lib/javascripts/moment_locale/kk.js +++ b/lib/javascripts/moment_locale/kk.js @@ -1,87 +1,87 @@ //! moment.js locale configuration -//! locale : kazakh (kk) +//! locale : Kazakh [kk] //! authors : Nurlan Rakhimzhanov : https://github.com/nurlan ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var suffixes = { - 0: '-ші', - 1: '-ші', - 2: '-ші', - 3: '-ші', - 4: '-ші', - 5: '-ші', - 6: '-шы', - 7: '-ші', - 8: '-ші', - 9: '-шы', - 10: '-шы', - 20: '-шы', - 30: '-шы', - 40: '-шы', - 50: '-ші', - 60: '-шы', - 70: '-ші', - 80: '-ші', - 90: '-шы', - 100: '-ші' - }; +var suffixes = { + 0: '-ші', + 1: '-ші', + 2: '-ші', + 3: '-ші', + 4: '-ші', + 5: '-ші', + 6: '-шы', + 7: '-ші', + 8: '-ші', + 9: '-шы', + 10: '-шы', + 20: '-шы', + 30: '-шы', + 40: '-шы', + 50: '-ші', + 60: '-шы', + 70: '-ші', + 80: '-ші', + 90: '-шы', + 100: '-ші' +}; - var kk = moment.defineLocale('kk', { - months : 'қаңтар_ақпан_наурыз_сәуір_мамыр_маусым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқсан'.split('_'), - monthsShort : 'қаң_ақп_нау_сәу_мам_мау_шіл_там_қыр_қаз_қар_жел'.split('_'), - weekdays : 'жексенбі_дүйсенбі_сейсенбі_сәрсенбі_бейсенбі_жұма_сенбі'.split('_'), - weekdaysShort : 'жек_дүй_сей_сәр_бей_жұм_сен'.split('_'), - weekdaysMin : 'жк_дй_сй_ср_бй_жм_сн'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Бүгін сағат] LT', - nextDay : '[Ертең сағат] LT', - nextWeek : 'dddd [сағат] LT', - lastDay : '[Кеше сағат] LT', - lastWeek : '[Өткен аптаның] dddd [сағат] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s ішінде', - past : '%s бұрын', - s : 'бірнеше секунд', - m : 'бір минут', - mm : '%d минут', - h : 'бір сағат', - hh : '%d сағат', - d : 'бір күн', - dd : '%d күн', - M : 'бір ай', - MM : '%d ай', - y : 'бір жыл', - yy : '%d жыл' - }, - ordinalParse: /\d{1,2}-(ші|шы)/, - ordinal : function (number) { - var a = number % 10, - b = number >= 100 ? 100 : null; - return number + (suffixes[number] || suffixes[a] || suffixes[b]); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); +var kk = moment.defineLocale('kk', { + months : 'қаңтар_ақпан_наурыз_сәуір_мамыр_маусым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқсан'.split('_'), + monthsShort : 'қаң_ақп_нау_сәу_мам_мау_шіл_там_қыр_қаз_қар_жел'.split('_'), + weekdays : 'жексенбі_дүйсенбі_сейсенбі_сәрсенбі_бейсенбі_жұма_сенбі'.split('_'), + weekdaysShort : 'жек_дүй_сей_сәр_бей_жұм_сен'.split('_'), + weekdaysMin : 'жк_дй_сй_ср_бй_жм_сн'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Бүгін сағат] LT', + nextDay : '[Ертең сағат] LT', + nextWeek : 'dddd [сағат] LT', + lastDay : '[Кеше сағат] LT', + lastWeek : '[Өткен аптаның] dddd [сағат] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s ішінде', + past : '%s бұрын', + s : 'бірнеше секунд', + m : 'бір минут', + mm : '%d минут', + h : 'бір сағат', + hh : '%d сағат', + d : 'бір күн', + dd : '%d күн', + M : 'бір ай', + MM : '%d ай', + y : 'бір жыл', + yy : '%d жыл' + }, + dayOfMonthOrdinalParse: /\d{1,2}-(ші|шы)/, + ordinal : function (number) { + var a = number % 10, + b = number >= 100 ? 100 : null; + return number + (suffixes[number] || suffixes[a] || suffixes[b]); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return kk; +return kk; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/km.js b/lib/javascripts/moment_locale/km.js old mode 100755 new mode 100644 index dcfd911c66..71482a7b52 --- a/lib/javascripts/moment_locale/km.js +++ b/lib/javascripts/moment_locale/km.js @@ -1,58 +1,58 @@ //! moment.js locale configuration -//! locale : khmer (km) +//! locale : Cambodian [km] //! author : Kruy Vanna : https://github.com/kruyvanna ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var km = moment.defineLocale('km', { - months: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split('_'), - monthsShort: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split('_'), - weekdays: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'), - weekdaysShort: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'), - weekdaysMin: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'), - longDateFormat: { - LT: 'HH:mm', - LTS : 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd, D MMMM YYYY HH:mm' - }, - calendar: { - sameDay: '[ថ្ងៃនេះ ម៉ោង] LT', - nextDay: '[ស្អែក ម៉ោង] LT', - nextWeek: 'dddd [ម៉ោង] LT', - lastDay: '[ម្សិលមិញ ម៉ោង] LT', - lastWeek: 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT', - sameElse: 'L' - }, - relativeTime: { - future: '%sទៀត', - past: '%sមុន', - s: 'ប៉ុន្មានវិនាទី', - m: 'មួយនាទី', - mm: '%d នាទី', - h: 'មួយម៉ោង', - hh: '%d ម៉ោង', - d: 'មួយថ្ងៃ', - dd: '%d ថ្ងៃ', - M: 'មួយខែ', - MM: '%d ខែ', - y: 'មួយឆ្នាំ', - yy: '%d ឆ្នាំ' - }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var km = moment.defineLocale('km', { + months: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split('_'), + monthsShort: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split('_'), + weekdays: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'), + weekdaysShort: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'), + weekdaysMin: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'), + longDateFormat: { + LT: 'HH:mm', + LTS : 'HH:mm:ss', + L: 'DD/MM/YYYY', + LL: 'D MMMM YYYY', + LLL: 'D MMMM YYYY HH:mm', + LLLL: 'dddd, D MMMM YYYY HH:mm' + }, + calendar: { + sameDay: '[ថ្ងៃនេះ ម៉ោង] LT', + nextDay: '[ស្អែក ម៉ោង] LT', + nextWeek: 'dddd [ម៉ោង] LT', + lastDay: '[ម្សិលមិញ ម៉ោង] LT', + lastWeek: 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT', + sameElse: 'L' + }, + relativeTime: { + future: '%sទៀត', + past: '%sមុន', + s: 'ប៉ុន្មានវិនាទី', + m: 'មួយនាទី', + mm: '%d នាទី', + h: 'មួយម៉ោង', + hh: '%d ម៉ោង', + d: 'មួយថ្ងៃ', + dd: '%d ថ្ងៃ', + M: 'មួយខែ', + MM: '%d ខែ', + y: 'មួយឆ្នាំ', + yy: '%d ឆ្នាំ' + }, + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return km; +return km; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/kn.js b/lib/javascripts/moment_locale/kn.js new file mode 100644 index 0000000000..aeacafa6b2 --- /dev/null +++ b/lib/javascripts/moment_locale/kn.js @@ -0,0 +1,126 @@ +//! moment.js locale configuration +//! locale : Kannada [kn] +//! author : Rajeev Naik : https://github.com/rajeevnaikte + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var symbolMap = { + '1': '೧', + '2': '೨', + '3': '೩', + '4': '೪', + '5': '೫', + '6': '೬', + '7': '೭', + '8': '೮', + '9': '೯', + '0': '೦' +}; +var numberMap = { + '೧': '1', + '೨': '2', + '೩': '3', + '೪': '4', + '೫': '5', + '೬': '6', + '೭': '7', + '೮': '8', + '೯': '9', + '೦': '0' +}; + +var kn = moment.defineLocale('kn', { + months : 'ಜನವರಿ_ಫೆಬ್ರವರಿ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂಬರ್_ಅಕ್ಟೋಬರ್_ನವೆಂಬರ್_ಡಿಸೆಂಬರ್'.split('_'), + monthsShort : 'ಜನ_ಫೆಬ್ರ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂಬ_ಅಕ್ಟೋಬ_ನವೆಂಬ_ಡಿಸೆಂಬ'.split('_'), + monthsParseExact: true, + weekdays : 'ಭಾನುವಾರ_ಸೋಮವಾರ_ಮಂಗಳವಾರ_ಬುಧವಾರ_ಗುರುವಾರ_ಶುಕ್ರವಾರ_ಶನಿವಾರ'.split('_'), + weekdaysShort : 'ಭಾನು_ಸೋಮ_ಮಂಗಳ_ಬುಧ_ಗುರು_ಶುಕ್ರ_ಶನಿ'.split('_'), + weekdaysMin : 'ಭಾ_ಸೋ_ಮಂ_ಬು_ಗು_ಶು_ಶ'.split('_'), + longDateFormat : { + LT : 'A h:mm', + LTS : 'A h:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm', + LLLL : 'dddd, D MMMM YYYY, A h:mm' + }, + calendar : { + sameDay : '[ಇಂದು] LT', + nextDay : '[ನಾಳೆ] LT', + nextWeek : 'dddd, LT', + lastDay : '[ನಿನ್ನೆ] LT', + lastWeek : '[ಕೊನೆಯ] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s ನಂತರ', + past : '%s ಹಿಂದೆ', + s : 'ಕೆಲವು ಕ್ಷಣಗಳು', + m : 'ಒಂದು ನಿಮಿಷ', + mm : '%d ನಿಮಿಷ', + h : 'ಒಂದು ಗಂಟೆ', + hh : '%d ಗಂಟೆ', + d : 'ಒಂದು ದಿನ', + dd : '%d ದಿನ', + M : 'ಒಂದು ತಿಂಗಳು', + MM : '%d ತಿಂಗಳು', + y : 'ಒಂದು ವರ್ಷ', + yy : '%d ವರ್ಷ' + }, + preparse: function (string) { + return string.replace(/[೧೨೩೪೫೬೭೮೯೦]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + meridiemParse: /ರಾತ್ರಿ|ಬೆಳಿಗ್ಗೆ|ಮಧ್ಯಾಹ್ನ|ಸಂಜೆ/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === 'ರಾತ್ರಿ') { + return hour < 4 ? hour : hour + 12; + } else if (meridiem === 'ಬೆಳಿಗ್ಗೆ') { + return hour; + } else if (meridiem === 'ಮಧ್ಯಾಹ್ನ') { + return hour >= 10 ? hour : hour + 12; + } else if (meridiem === 'ಸಂಜೆ') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'ರಾತ್ರಿ'; + } else if (hour < 10) { + return 'ಬೆಳಿಗ್ಗೆ'; + } else if (hour < 17) { + return 'ಮಧ್ಯಾಹ್ನ'; + } else if (hour < 20) { + return 'ಸಂಜೆ'; + } else { + return 'ರಾತ್ರಿ'; + } + }, + dayOfMonthOrdinalParse: /\d{1,2}(ನೇ)/, + ordinal : function (number) { + return number + 'ನೇ'; + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); + +return kn; + +}))); diff --git a/lib/javascripts/moment_locale/ko.js b/lib/javascripts/moment_locale/ko.js old mode 100755 new mode 100644 index 0481987a4e..13d1c4d300 --- a/lib/javascripts/moment_locale/ko.js +++ b/lib/javascripts/moment_locale/ko.js @@ -1,68 +1,69 @@ //! moment.js locale configuration -//! locale : korean (ko) -//! -//! authors -//! -//! - Kyungwook, Park : https://github.com/kyungw00k -//! - Jeeeyul Lee +//! locale : Korean [ko] +//! author : Kyungwook, Park : https://github.com/kyungw00k +//! author : Jeeeyul Lee ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ko = moment.defineLocale('ko', { - months : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), - monthsShort : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), - weekdays : '일요일_월요일_화요일_수요일_목요일_금요일_토요일'.split('_'), - weekdaysShort : '일_월_화_수_목_금_토'.split('_'), - weekdaysMin : '일_월_화_수_목_금_토'.split('_'), - longDateFormat : { - LT : 'A h시 m분', - LTS : 'A h시 m분 s초', - L : 'YYYY.MM.DD', - LL : 'YYYY년 MMMM D일', - LLL : 'YYYY년 MMMM D일 A h시 m분', - LLLL : 'YYYY년 MMMM D일 dddd A h시 m분' - }, - calendar : { - sameDay : '오늘 LT', - nextDay : '내일 LT', - nextWeek : 'dddd LT', - lastDay : '어제 LT', - lastWeek : '지난주 dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s 후', - past : '%s 전', - s : '몇 초', - ss : '%d초', - m : '일분', - mm : '%d분', - h : '한 시간', - hh : '%d시간', - d : '하루', - dd : '%d일', - M : '한 달', - MM : '%d달', - y : '일 년', - yy : '%d년' - }, - ordinalParse : /\d{1,2}일/, - ordinal : '%d일', - meridiemParse : /오전|오후/, - isPM : function (token) { - return token === '오후'; - }, - meridiem : function (hour, minute, isUpper) { - return hour < 12 ? '오전' : '오후'; - } - }); +var ko = moment.defineLocale('ko', { + months : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), + monthsShort : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), + weekdays : '일요일_월요일_화요일_수요일_목요일_금요일_토요일'.split('_'), + weekdaysShort : '일_월_화_수_목_금_토'.split('_'), + weekdaysMin : '일_월_화_수_목_금_토'.split('_'), + longDateFormat : { + LT : 'A h:mm', + LTS : 'A h:mm:ss', + L : 'YYYY.MM.DD', + LL : 'YYYY년 MMMM D일', + LLL : 'YYYY년 MMMM D일 A h:mm', + LLLL : 'YYYY년 MMMM D일 dddd A h:mm', + l : 'YYYY.MM.DD', + ll : 'YYYY년 MMMM D일', + lll : 'YYYY년 MMMM D일 A h:mm', + llll : 'YYYY년 MMMM D일 dddd A h:mm' + }, + calendar : { + sameDay : '오늘 LT', + nextDay : '내일 LT', + nextWeek : 'dddd LT', + lastDay : '어제 LT', + lastWeek : '지난주 dddd LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s 후', + past : '%s 전', + s : '몇 초', + ss : '%d초', + m : '1분', + mm : '%d분', + h : '한 시간', + hh : '%d시간', + d : '하루', + dd : '%d일', + M : '한 달', + MM : '%d달', + y : '일 년', + yy : '%d년' + }, + dayOfMonthOrdinalParse : /\d{1,2}일/, + ordinal : '%d일', + meridiemParse : /오전|오후/, + isPM : function (token) { + return token === '오후'; + }, + meridiem : function (hour, minute, isUpper) { + return hour < 12 ? '오전' : '오후'; + } +}); - return ko; +return ko; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ky.js b/lib/javascripts/moment_locale/ky.js new file mode 100644 index 0000000000..ae4053a339 --- /dev/null +++ b/lib/javascripts/moment_locale/ky.js @@ -0,0 +1,88 @@ +//! moment.js locale configuration +//! locale : Kyrgyz [ky] +//! author : Chyngyz Arystan uulu : https://github.com/chyngyz + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + + +var suffixes = { + 0: '-чү', + 1: '-чи', + 2: '-чи', + 3: '-чү', + 4: '-чү', + 5: '-чи', + 6: '-чы', + 7: '-чи', + 8: '-чи', + 9: '-чу', + 10: '-чу', + 20: '-чы', + 30: '-чу', + 40: '-чы', + 50: '-чү', + 60: '-чы', + 70: '-чи', + 80: '-чи', + 90: '-чу', + 100: '-чү' +}; + +var ky = moment.defineLocale('ky', { + months : 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'), + monthsShort : 'янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек'.split('_'), + weekdays : 'Жекшемби_Дүйшөмбү_Шейшемби_Шаршемби_Бейшемби_Жума_Ишемби'.split('_'), + weekdaysShort : 'Жек_Дүй_Шей_Шар_Бей_Жум_Ише'.split('_'), + weekdaysMin : 'Жк_Дй_Шй_Шр_Бй_Жм_Иш'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Бүгүн саат] LT', + nextDay : '[Эртең саат] LT', + nextWeek : 'dddd [саат] LT', + lastDay : '[Кече саат] LT', + lastWeek : '[Өткен аптанын] dddd [күнү] [саат] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s ичинде', + past : '%s мурун', + s : 'бирнече секунд', + m : 'бир мүнөт', + mm : '%d мүнөт', + h : 'бир саат', + hh : '%d саат', + d : 'бир күн', + dd : '%d күн', + M : 'бир ай', + MM : '%d ай', + y : 'бир жыл', + yy : '%d жыл' + }, + dayOfMonthOrdinalParse: /\d{1,2}-(чи|чы|чү|чу)/, + ordinal : function (number) { + var a = number % 10, + b = number >= 100 ? 100 : null; + return number + (suffixes[number] || suffixes[a] || suffixes[b]); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); + +return ky; + +}))); diff --git a/lib/javascripts/moment_locale/lb.js b/lib/javascripts/moment_locale/lb.js old mode 100755 new mode 100644 index bd51b39b70..56c1803900 --- a/lib/javascripts/moment_locale/lb.js +++ b/lib/javascripts/moment_locale/lb.js @@ -1,136 +1,137 @@ //! moment.js locale configuration -//! locale : Luxembourgish (lb) -//! author : mweimerskirch : https://github.com/mweimerskirch, David Raison : https://github.com/kwisatz +//! locale : Luxembourgish [lb] +//! author : mweimerskirch : https://github.com/mweimerskirch +//! author : David Raison : https://github.com/kwisatz ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function processRelativeTime(number, withoutSuffix, key, isFuture) { - var format = { - 'm': ['eng Minutt', 'enger Minutt'], - 'h': ['eng Stonn', 'enger Stonn'], - 'd': ['een Dag', 'engem Dag'], - 'M': ['ee Mount', 'engem Mount'], - 'y': ['ee Joer', 'engem Joer'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 'm': ['eng Minutt', 'enger Minutt'], + 'h': ['eng Stonn', 'enger Stonn'], + 'd': ['een Dag', 'engem Dag'], + 'M': ['ee Mount', 'engem Mount'], + 'y': ['ee Joer', 'engem Joer'] + }; + return withoutSuffix ? format[key][0] : format[key][1]; +} +function processFutureTime(string) { + var number = string.substr(0, string.indexOf(' ')); + if (eifelerRegelAppliesToNumber(number)) { + return 'a ' + string; } - function processFutureTime(string) { - var number = string.substr(0, string.indexOf(' ')); - if (eifelerRegelAppliesToNumber(number)) { - return 'a ' + string; - } - return 'an ' + string; + return 'an ' + string; +} +function processPastTime(string) { + var number = string.substr(0, string.indexOf(' ')); + if (eifelerRegelAppliesToNumber(number)) { + return 'viru ' + string; } - function processPastTime(string) { - var number = string.substr(0, string.indexOf(' ')); - if (eifelerRegelAppliesToNumber(number)) { - return 'viru ' + string; - } - return 'virun ' + string; + return 'virun ' + string; +} +/** + * Returns true if the word before the given number loses the '-n' ending. + * e.g. 'an 10 Deeg' but 'a 5 Deeg' + * + * @param number {integer} + * @returns {boolean} + */ +function eifelerRegelAppliesToNumber(number) { + number = parseInt(number, 10); + if (isNaN(number)) { + return false; } - /** - * Returns true if the word before the given number loses the '-n' ending. - * e.g. 'an 10 Deeg' but 'a 5 Deeg' - * - * @param number {integer} - * @returns {boolean} - */ - function eifelerRegelAppliesToNumber(number) { - number = parseInt(number, 10); - if (isNaN(number)) { - return false; - } - if (number < 0) { - // Negative Number --> always true + if (number < 0) { + // Negative Number --> always true + return true; + } else if (number < 10) { + // Only 1 digit + if (4 <= number && number <= 7) { return true; - } else if (number < 10) { - // Only 1 digit - if (4 <= number && number <= 7) { - return true; - } - return false; - } else if (number < 100) { - // 2 digits - var lastDigit = number % 10, firstDigit = number / 10; - if (lastDigit === 0) { - return eifelerRegelAppliesToNumber(firstDigit); - } - return eifelerRegelAppliesToNumber(lastDigit); - } else if (number < 10000) { - // 3 or 4 digits --> recursively check first digit - while (number >= 10) { - number = number / 10; - } - return eifelerRegelAppliesToNumber(number); - } else { - // Anything larger than 4 digits: recursively check first n-3 digits - number = number / 1000; - return eifelerRegelAppliesToNumber(number); } + return false; + } else if (number < 100) { + // 2 digits + var lastDigit = number % 10, firstDigit = number / 10; + if (lastDigit === 0) { + return eifelerRegelAppliesToNumber(firstDigit); + } + return eifelerRegelAppliesToNumber(lastDigit); + } else if (number < 10000) { + // 3 or 4 digits --> recursively check first digit + while (number >= 10) { + number = number / 10; + } + return eifelerRegelAppliesToNumber(number); + } else { + // Anything larger than 4 digits: recursively check first n-3 digits + number = number / 1000; + return eifelerRegelAppliesToNumber(number); } +} - var lb = moment.defineLocale('lb', { - months: 'Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), - monthsShort: 'Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays: 'Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg'.split('_'), - weekdaysShort: 'So._Mé._Dë._Më._Do._Fr._Sa.'.split('_'), - weekdaysMin: 'So_Mé_Dë_Më_Do_Fr_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm [Auer]', - LTS: 'H:mm:ss [Auer]', - L: 'DD.MM.YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm [Auer]', - LLLL: 'dddd, D. MMMM YYYY H:mm [Auer]' - }, - calendar: { - sameDay: '[Haut um] LT', - sameElse: 'L', - nextDay: '[Muer um] LT', - nextWeek: 'dddd [um] LT', - lastDay: '[Gëschter um] LT', - lastWeek: function () { - // Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule - switch (this.day()) { - case 2: - case 4: - return '[Leschten] dddd [um] LT'; - default: - return '[Leschte] dddd [um] LT'; - } +var lb = moment.defineLocale('lb', { + months: 'Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), + monthsShort: 'Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'), + monthsParseExact : true, + weekdays: 'Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg'.split('_'), + weekdaysShort: 'So._Mé._Dë._Më._Do._Fr._Sa.'.split('_'), + weekdaysMin: 'So_Mé_Dë_Më_Do_Fr_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat: { + LT: 'H:mm [Auer]', + LTS: 'H:mm:ss [Auer]', + L: 'DD.MM.YYYY', + LL: 'D. MMMM YYYY', + LLL: 'D. MMMM YYYY H:mm [Auer]', + LLLL: 'dddd, D. MMMM YYYY H:mm [Auer]' + }, + calendar: { + sameDay: '[Haut um] LT', + sameElse: 'L', + nextDay: '[Muer um] LT', + nextWeek: 'dddd [um] LT', + lastDay: '[Gëschter um] LT', + lastWeek: function () { + // Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule + switch (this.day()) { + case 2: + case 4: + return '[Leschten] dddd [um] LT'; + default: + return '[Leschte] dddd [um] LT'; } - }, - relativeTime : { - future : processFutureTime, - past : processPastTime, - s : 'e puer Sekonnen', - m : processRelativeTime, - mm : '%d Minutten', - h : processRelativeTime, - hh : '%d Stonnen', - d : processRelativeTime, - dd : '%d Deeg', - M : processRelativeTime, - MM : '%d Méint', - y : processRelativeTime, - yy : '%d Joer' - }, - ordinalParse: /\d{1,2}\./, - ordinal: '%d.', - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 4th is the first week of the year. } - }); + }, + relativeTime : { + future : processFutureTime, + past : processPastTime, + s : 'e puer Sekonnen', + m : processRelativeTime, + mm : '%d Minutten', + h : processRelativeTime, + hh : '%d Stonnen', + d : processRelativeTime, + dd : '%d Deeg', + M : processRelativeTime, + MM : '%d Méint', + y : processRelativeTime, + yy : '%d Joer' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal: '%d.', + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return lb; +return lb; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/lo.js b/lib/javascripts/moment_locale/lo.js old mode 100755 new mode 100644 index 9171fd7486..8e5cdbddf2 --- a/lib/javascripts/moment_locale/lo.js +++ b/lib/javascripts/moment_locale/lo.js @@ -1,70 +1,70 @@ //! moment.js locale configuration -//! locale : lao (lo) +//! locale : Lao [lo] //! author : Ryan Hart : https://github.com/ryanhart2 ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var lo = moment.defineLocale('lo', { - months : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'), - monthsShort : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'), - weekdays : 'ອາທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'), - weekdaysShort : 'ທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'), - weekdaysMin : 'ທ_ຈ_ອຄ_ພ_ພຫ_ສກ_ສ'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'ວັນdddd D MMMM YYYY HH:mm' - }, - meridiemParse: /ຕອນເຊົ້າ|ຕອນແລງ/, - isPM: function (input) { - return input === 'ຕອນແລງ'; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ຕອນເຊົ້າ'; - } else { - return 'ຕອນແລງ'; - } - }, - calendar : { - sameDay : '[ມື້ນີ້ເວລາ] LT', - nextDay : '[ມື້ອື່ນເວລາ] LT', - nextWeek : '[ວັນ]dddd[ໜ້າເວລາ] LT', - lastDay : '[ມື້ວານນີ້ເວລາ] LT', - lastWeek : '[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'ອີກ %s', - past : '%sຜ່ານມາ', - s : 'ບໍ່ເທົ່າໃດວິນາທີ', - m : '1 ນາທີ', - mm : '%d ນາທີ', - h : '1 ຊົ່ວໂມງ', - hh : '%d ຊົ່ວໂມງ', - d : '1 ມື້', - dd : '%d ມື້', - M : '1 ເດືອນ', - MM : '%d ເດືອນ', - y : '1 ປີ', - yy : '%d ປີ' - }, - ordinalParse: /(ທີ່)\d{1,2}/, - ordinal : function (number) { - return 'ທີ່' + number; +var lo = moment.defineLocale('lo', { + months : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'), + monthsShort : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'), + weekdays : 'ອາທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'), + weekdaysShort : 'ທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'), + weekdaysMin : 'ທ_ຈ_ອຄ_ພ_ພຫ_ສກ_ສ'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'ວັນdddd D MMMM YYYY HH:mm' + }, + meridiemParse: /ຕອນເຊົ້າ|ຕອນແລງ/, + isPM: function (input) { + return input === 'ຕອນແລງ'; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'ຕອນເຊົ້າ'; + } else { + return 'ຕອນແລງ'; } - }); + }, + calendar : { + sameDay : '[ມື້ນີ້ເວລາ] LT', + nextDay : '[ມື້ອື່ນເວລາ] LT', + nextWeek : '[ວັນ]dddd[ໜ້າເວລາ] LT', + lastDay : '[ມື້ວານນີ້ເວລາ] LT', + lastWeek : '[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'ອີກ %s', + past : '%sຜ່ານມາ', + s : 'ບໍ່ເທົ່າໃດວິນາທີ', + m : '1 ນາທີ', + mm : '%d ນາທີ', + h : '1 ຊົ່ວໂມງ', + hh : '%d ຊົ່ວໂມງ', + d : '1 ມື້', + dd : '%d ມື້', + M : '1 ເດືອນ', + MM : '%d ເດືອນ', + y : '1 ປີ', + yy : '%d ປີ' + }, + dayOfMonthOrdinalParse: /(ທີ່)\d{1,2}/, + ordinal : function (number) { + return 'ທີ່' + number; + } +}); - return lo; +return lo; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/lt.js b/lib/javascripts/moment_locale/lt.js old mode 100755 new mode 100644 index a3f05d440c..d2fb760330 --- a/lib/javascripts/moment_locale/lt.js +++ b/lib/javascripts/moment_locale/lt.js @@ -1,116 +1,117 @@ //! moment.js locale configuration -//! locale : Lithuanian (lt) +//! locale : Lithuanian [lt] //! author : Mindaugas Mozūras : https://github.com/mmozuras ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var units = { - 'm' : 'minutė_minutės_minutę', - 'mm': 'minutės_minučių_minutes', - 'h' : 'valanda_valandos_valandą', - 'hh': 'valandos_valandų_valandas', - 'd' : 'diena_dienos_dieną', - 'dd': 'dienos_dienų_dienas', - 'M' : 'mėnuo_mėnesio_mėnesį', - 'MM': 'mėnesiai_mėnesių_mėnesius', - 'y' : 'metai_metų_metus', - 'yy': 'metai_metų_metus' - }; - function translateSeconds(number, withoutSuffix, key, isFuture) { - if (withoutSuffix) { - return 'kelios sekundės'; +var units = { + 'm' : 'minutė_minutės_minutę', + 'mm': 'minutės_minučių_minutes', + 'h' : 'valanda_valandos_valandą', + 'hh': 'valandos_valandų_valandas', + 'd' : 'diena_dienos_dieną', + 'dd': 'dienos_dienų_dienas', + 'M' : 'mėnuo_mėnesio_mėnesį', + 'MM': 'mėnesiai_mėnesių_mėnesius', + 'y' : 'metai_metų_metus', + 'yy': 'metai_metų_metus' +}; +function translateSeconds(number, withoutSuffix, key, isFuture) { + if (withoutSuffix) { + return 'kelios sekundės'; + } else { + return isFuture ? 'kelių sekundžių' : 'kelias sekundes'; + } +} +function translateSingular(number, withoutSuffix, key, isFuture) { + return withoutSuffix ? forms(key)[0] : (isFuture ? forms(key)[1] : forms(key)[2]); +} +function special(number) { + return number % 10 === 0 || (number > 10 && number < 20); +} +function forms(key) { + return units[key].split('_'); +} +function translate(number, withoutSuffix, key, isFuture) { + var result = number + ' '; + if (number === 1) { + return result + translateSingular(number, withoutSuffix, key[0], isFuture); + } else if (withoutSuffix) { + return result + (special(number) ? forms(key)[1] : forms(key)[0]); + } else { + if (isFuture) { + return result + forms(key)[1]; } else { - return isFuture ? 'kelių sekundžių' : 'kelias sekundes'; + return result + (special(number) ? forms(key)[1] : forms(key)[2]); } } - function translateSingular(number, withoutSuffix, key, isFuture) { - return withoutSuffix ? forms(key)[0] : (isFuture ? forms(key)[1] : forms(key)[2]); +} +var lt = moment.defineLocale('lt', { + months : { + format: 'sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio'.split('_'), + standalone: 'sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjūtis_rugsėjis_spalis_lapkritis_gruodis'.split('_'), + isFormat: /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/ + }, + monthsShort : 'sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd'.split('_'), + weekdays : { + format: 'sekmadienį_pirmadienį_antradienį_trečiadienį_ketvirtadienį_penktadienį_šeštadienį'.split('_'), + standalone: 'sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis'.split('_'), + isFormat: /dddd HH:mm/ + }, + weekdaysShort : 'Sek_Pir_Ant_Tre_Ket_Pen_Šeš'.split('_'), + weekdaysMin : 'S_P_A_T_K_Pn_Š'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY-MM-DD', + LL : 'YYYY [m.] MMMM D [d.]', + LLL : 'YYYY [m.] MMMM D [d.], HH:mm [val.]', + LLLL : 'YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]', + l : 'YYYY-MM-DD', + ll : 'YYYY [m.] MMMM D [d.]', + lll : 'YYYY [m.] MMMM D [d.], HH:mm [val.]', + llll : 'YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]' + }, + calendar : { + sameDay : '[Šiandien] LT', + nextDay : '[Rytoj] LT', + nextWeek : 'dddd LT', + lastDay : '[Vakar] LT', + lastWeek : '[Praėjusį] dddd LT', + sameElse : 'L' + }, + relativeTime : { + future : 'po %s', + past : 'prieš %s', + s : translateSeconds, + m : translateSingular, + mm : translate, + h : translateSingular, + hh : translate, + d : translateSingular, + dd : translate, + M : translateSingular, + MM : translate, + y : translateSingular, + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}-oji/, + ordinal : function (number) { + return number + '-oji'; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } - function special(number) { - return number % 10 === 0 || (number > 10 && number < 20); - } - function forms(key) { - return units[key].split('_'); - } - function translate(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - if (number === 1) { - return result + translateSingular(number, withoutSuffix, key[0], isFuture); - } else if (withoutSuffix) { - return result + (special(number) ? forms(key)[1] : forms(key)[0]); - } else { - if (isFuture) { - return result + forms(key)[1]; - } else { - return result + (special(number) ? forms(key)[1] : forms(key)[2]); - } - } - } - var lt = moment.defineLocale('lt', { - months : { - format: 'sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio'.split('_'), - standalone: 'sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjūtis_rugsėjis_spalis_lapkritis_gruodis'.split('_') - }, - monthsShort : 'sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd'.split('_'), - weekdays : { - format: 'sekmadienį_pirmadienį_antradienį_trečiadienį_ketvirtadienį_penktadienį_šeštadienį'.split('_'), - standalone: 'sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis'.split('_'), - isFormat: /dddd HH:mm/ - }, - weekdaysShort : 'Sek_Pir_Ant_Tre_Ket_Pen_Šeš'.split('_'), - weekdaysMin : 'S_P_A_T_K_Pn_Š'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'YYYY [m.] MMMM D [d.]', - LLL : 'YYYY [m.] MMMM D [d.], HH:mm [val.]', - LLLL : 'YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]', - l : 'YYYY-MM-DD', - ll : 'YYYY [m.] MMMM D [d.]', - lll : 'YYYY [m.] MMMM D [d.], HH:mm [val.]', - llll : 'YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]' - }, - calendar : { - sameDay : '[Šiandien] LT', - nextDay : '[Rytoj] LT', - nextWeek : 'dddd LT', - lastDay : '[Vakar] LT', - lastWeek : '[Praėjusį] dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : 'po %s', - past : 'prieš %s', - s : translateSeconds, - m : translateSingular, - mm : translate, - h : translateSingular, - hh : translate, - d : translateSingular, - dd : translate, - M : translateSingular, - MM : translate, - y : translateSingular, - yy : translate - }, - ordinalParse: /\d{1,2}-oji/, - ordinal : function (number) { - return number + '-oji'; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +}); - return lt; +return lt; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/lv.js b/lib/javascripts/moment_locale/lv.js old mode 100755 new mode 100644 index 21e00b0c02..e2d988791a --- a/lib/javascripts/moment_locale/lv.js +++ b/lib/javascripts/moment_locale/lv.js @@ -1,97 +1,97 @@ //! moment.js locale configuration -//! locale : latvian (lv) +//! locale : Latvian [lv] //! author : Kristaps Karlsons : https://github.com/skakri //! author : Jānis Elmeris : https://github.com/JanisE ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var units = { - 'm': 'minūtes_minūtēm_minūte_minūtes'.split('_'), - 'mm': 'minūtes_minūtēm_minūte_minūtes'.split('_'), - 'h': 'stundas_stundām_stunda_stundas'.split('_'), - 'hh': 'stundas_stundām_stunda_stundas'.split('_'), - 'd': 'dienas_dienām_diena_dienas'.split('_'), - 'dd': 'dienas_dienām_diena_dienas'.split('_'), - 'M': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'), - 'MM': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'), - 'y': 'gada_gadiem_gads_gadi'.split('_'), - 'yy': 'gada_gadiem_gads_gadi'.split('_') - }; - /** - * @param withoutSuffix boolean true = a length of time; false = before/after a period of time. - */ - function format(forms, number, withoutSuffix) { - if (withoutSuffix) { - // E.g. "21 minūte", "3 minūtes". - return number % 10 === 1 && number !== 11 ? forms[2] : forms[3]; - } else { - // E.g. "21 minūtes" as in "pēc 21 minūtes". - // E.g. "3 minūtēm" as in "pēc 3 minūtēm". - return number % 10 === 1 && number !== 11 ? forms[0] : forms[1]; - } - } - function relativeTimeWithPlural(number, withoutSuffix, key) { - return number + ' ' + format(units[key], number, withoutSuffix); - } - function relativeTimeWithSingular(number, withoutSuffix, key) { - return format(units[key], number, withoutSuffix); - } - function relativeSeconds(number, withoutSuffix) { - return withoutSuffix ? 'dažas sekundes' : 'dažām sekundēm'; +var units = { + 'm': 'minūtes_minūtēm_minūte_minūtes'.split('_'), + 'mm': 'minūtes_minūtēm_minūte_minūtes'.split('_'), + 'h': 'stundas_stundām_stunda_stundas'.split('_'), + 'hh': 'stundas_stundām_stunda_stundas'.split('_'), + 'd': 'dienas_dienām_diena_dienas'.split('_'), + 'dd': 'dienas_dienām_diena_dienas'.split('_'), + 'M': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'), + 'MM': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'), + 'y': 'gada_gadiem_gads_gadi'.split('_'), + 'yy': 'gada_gadiem_gads_gadi'.split('_') +}; +/** + * @param withoutSuffix boolean true = a length of time; false = before/after a period of time. + */ +function format(forms, number, withoutSuffix) { + if (withoutSuffix) { + // E.g. "21 minūte", "3 minūtes". + return number % 10 === 1 && number % 100 !== 11 ? forms[2] : forms[3]; + } else { + // E.g. "21 minūtes" as in "pēc 21 minūtes". + // E.g. "3 minūtēm" as in "pēc 3 minūtēm". + return number % 10 === 1 && number % 100 !== 11 ? forms[0] : forms[1]; } +} +function relativeTimeWithPlural(number, withoutSuffix, key) { + return number + ' ' + format(units[key], number, withoutSuffix); +} +function relativeTimeWithSingular(number, withoutSuffix, key) { + return format(units[key], number, withoutSuffix); +} +function relativeSeconds(number, withoutSuffix) { + return withoutSuffix ? 'dažas sekundes' : 'dažām sekundēm'; +} - var lv = moment.defineLocale('lv', { - months : 'janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris'.split('_'), - monthsShort : 'jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec'.split('_'), - weekdays : 'svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena'.split('_'), - weekdaysShort : 'Sv_P_O_T_C_Pk_S'.split('_'), - weekdaysMin : 'Sv_P_O_T_C_Pk_S'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY.', - LL : 'YYYY. [gada] D. MMMM', - LLL : 'YYYY. [gada] D. MMMM, HH:mm', - LLLL : 'YYYY. [gada] D. MMMM, dddd, HH:mm' - }, - calendar : { - sameDay : '[Šodien pulksten] LT', - nextDay : '[Rīt pulksten] LT', - nextWeek : 'dddd [pulksten] LT', - lastDay : '[Vakar pulksten] LT', - lastWeek : '[Pagājušā] dddd [pulksten] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'pēc %s', - past : 'pirms %s', - s : relativeSeconds, - m : relativeTimeWithSingular, - mm : relativeTimeWithPlural, - h : relativeTimeWithSingular, - hh : relativeTimeWithPlural, - d : relativeTimeWithSingular, - dd : relativeTimeWithPlural, - M : relativeTimeWithSingular, - MM : relativeTimeWithPlural, - y : relativeTimeWithSingular, - yy : relativeTimeWithPlural - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var lv = moment.defineLocale('lv', { + months : 'janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris'.split('_'), + monthsShort : 'jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec'.split('_'), + weekdays : 'svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena'.split('_'), + weekdaysShort : 'Sv_P_O_T_C_Pk_S'.split('_'), + weekdaysMin : 'Sv_P_O_T_C_Pk_S'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY.', + LL : 'YYYY. [gada] D. MMMM', + LLL : 'YYYY. [gada] D. MMMM, HH:mm', + LLLL : 'YYYY. [gada] D. MMMM, dddd, HH:mm' + }, + calendar : { + sameDay : '[Šodien pulksten] LT', + nextDay : '[Rīt pulksten] LT', + nextWeek : 'dddd [pulksten] LT', + lastDay : '[Vakar pulksten] LT', + lastWeek : '[Pagājušā] dddd [pulksten] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'pēc %s', + past : 'pirms %s', + s : relativeSeconds, + m : relativeTimeWithSingular, + mm : relativeTimeWithPlural, + h : relativeTimeWithSingular, + hh : relativeTimeWithPlural, + d : relativeTimeWithSingular, + dd : relativeTimeWithPlural, + M : relativeTimeWithSingular, + MM : relativeTimeWithPlural, + y : relativeTimeWithSingular, + yy : relativeTimeWithPlural + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return lv; +return lv; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/me.js b/lib/javascripts/moment_locale/me.js old mode 100755 new mode 100644 index a78cf5a94f..07ed6ca2a5 --- a/lib/javascripts/moment_locale/me.js +++ b/lib/javascripts/moment_locale/me.js @@ -1,60 +1,60 @@ //! moment.js locale configuration -//! locale : Montenegrin (me) +//! locale : Montenegrin [me] //! author : Miodrag Nikač : https://github.com/miodragnikac ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var translator = { - words: { //Different grammatical cases - m: ['jedan minut', 'jednog minuta'], - mm: ['minut', 'minuta', 'minuta'], - h: ['jedan sat', 'jednog sata'], - hh: ['sat', 'sata', 'sati'], - dd: ['dan', 'dana', 'dana'], - MM: ['mjesec', 'mjeseca', 'mjeseci'], - yy: ['godina', 'godine', 'godina'] - }, - correctGrammaticalCase: function (number, wordKey) { - return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); - }, - translate: function (number, withoutSuffix, key) { - var wordKey = translator.words[key]; - if (key.length === 1) { - return withoutSuffix ? wordKey[0] : wordKey[1]; - } else { - return number + ' ' + translator.correctGrammaticalCase(number, wordKey); - } +var translator = { + words: { //Different grammatical cases + m: ['jedan minut', 'jednog minuta'], + mm: ['minut', 'minuta', 'minuta'], + h: ['jedan sat', 'jednog sata'], + hh: ['sat', 'sata', 'sati'], + dd: ['dan', 'dana', 'dana'], + MM: ['mjesec', 'mjeseca', 'mjeseci'], + yy: ['godina', 'godine', 'godina'] + }, + correctGrammaticalCase: function (number, wordKey) { + return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); + }, + translate: function (number, withoutSuffix, key) { + var wordKey = translator.words[key]; + if (key.length === 1) { + return withoutSuffix ? wordKey[0] : wordKey[1]; + } else { + return number + ' ' + translator.correctGrammaticalCase(number, wordKey); } - }; + } +}; - var me = moment.defineLocale('me', { - months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'), - monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'), - monthsParseExact : true, - weekdays: 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), - weekdaysShort: 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), - weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm', - LTS : 'H:mm:ss', - L: 'DD. MM. YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' - }, - calendar: { - sameDay: '[danas u] LT', - nextDay: '[sjutra u] LT', +var me = moment.defineLocale('me', { + months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'), + monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'), + monthsParseExact : true, + weekdays: 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), + weekdaysShort: 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), + weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'), + weekdaysParseExact : true, + longDateFormat: { + LT: 'H:mm', + LTS : 'H:mm:ss', + L: 'DD.MM.YYYY', + LL: 'D. MMMM YYYY', + LLL: 'D. MMMM YYYY H:mm', + LLLL: 'dddd, D. MMMM YYYY H:mm' + }, + calendar: { + sameDay: '[danas u] LT', + nextDay: '[sjutra u] LT', - nextWeek: function () { - switch (this.day()) { + nextWeek: function () { + switch (this.day()) { case 0: return '[u] [nedjelju] [u] LT'; case 3: @@ -66,46 +66,46 @@ case 4: case 5: return '[u] dddd [u] LT'; - } - }, - lastDay : '[juče u] LT', - lastWeek : function () { - var lastWeekDays = [ - '[prošle] [nedjelje] [u] LT', - '[prošlog] [ponedjeljka] [u] LT', - '[prošlog] [utorka] [u] LT', - '[prošle] [srijede] [u] LT', - '[prošlog] [četvrtka] [u] LT', - '[prošlog] [petka] [u] LT', - '[prošle] [subote] [u] LT' - ]; - return lastWeekDays[this.day()]; - }, - sameElse : 'L' + } }, - relativeTime : { - future : 'za %s', - past : 'prije %s', - s : 'nekoliko sekundi', - m : translator.translate, - mm : translator.translate, - h : translator.translate, - hh : translator.translate, - d : 'dan', - dd : translator.translate, - M : 'mjesec', - MM : translator.translate, - y : 'godinu', - yy : translator.translate + lastDay : '[juče u] LT', + lastWeek : function () { + var lastWeekDays = [ + '[prošle] [nedjelje] [u] LT', + '[prošlog] [ponedjeljka] [u] LT', + '[prošlog] [utorka] [u] LT', + '[prošle] [srijede] [u] LT', + '[prošlog] [četvrtka] [u] LT', + '[prošlog] [petka] [u] LT', + '[prošle] [subote] [u] LT' + ]; + return lastWeekDays[this.day()]; }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + sameElse : 'L' + }, + relativeTime : { + future : 'za %s', + past : 'prije %s', + s : 'nekoliko sekundi', + m : translator.translate, + mm : translator.translate, + h : translator.translate, + hh : translator.translate, + d : 'dan', + dd : translator.translate, + M : 'mjesec', + MM : translator.translate, + y : 'godinu', + yy : translator.translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return me; +return me; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/mi.js b/lib/javascripts/moment_locale/mi.js new file mode 100644 index 0000000000..c5cce374b7 --- /dev/null +++ b/lib/javascripts/moment_locale/mi.js @@ -0,0 +1,64 @@ +//! moment.js locale configuration +//! locale : Maori [mi] +//! author : John Corrigan : https://github.com/johnideal + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var mi = moment.defineLocale('mi', { + months: 'Kohi-tāte_Hui-tanguru_Poutū-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea'.split('_'), + monthsShort: 'Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki'.split('_'), + monthsRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i, + monthsStrictRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i, + monthsShortRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i, + monthsShortStrictRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i, + weekdays: 'Rātapu_Mane_Tūrei_Wenerei_Tāite_Paraire_Hātarei'.split('_'), + weekdaysShort: 'Ta_Ma_Tū_We_Tāi_Pa_Hā'.split('_'), + weekdaysMin: 'Ta_Ma_Tū_We_Tāi_Pa_Hā'.split('_'), + longDateFormat: { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L: 'DD/MM/YYYY', + LL: 'D MMMM YYYY', + LLL: 'D MMMM YYYY [i] HH:mm', + LLLL: 'dddd, D MMMM YYYY [i] HH:mm' + }, + calendar: { + sameDay: '[i teie mahana, i] LT', + nextDay: '[apopo i] LT', + nextWeek: 'dddd [i] LT', + lastDay: '[inanahi i] LT', + lastWeek: 'dddd [whakamutunga i] LT', + sameElse: 'L' + }, + relativeTime: { + future: 'i roto i %s', + past: '%s i mua', + s: 'te hēkona ruarua', + m: 'he meneti', + mm: '%d meneti', + h: 'te haora', + hh: '%d haora', + d: 'he ra', + dd: '%d ra', + M: 'he marama', + MM: '%d marama', + y: 'he tau', + yy: '%d tau' + }, + dayOfMonthOrdinalParse: /\d{1,2}º/, + ordinal: '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return mi; + +}))); diff --git a/lib/javascripts/moment_locale/mk.js b/lib/javascripts/moment_locale/mk.js old mode 100755 new mode 100644 index 89b5414c11..cdfb1e1c88 --- a/lib/javascripts/moment_locale/mk.js +++ b/lib/javascripts/moment_locale/mk.js @@ -1,36 +1,36 @@ //! moment.js locale configuration -//! locale : macedonian (mk) +//! locale : Macedonian [mk] //! author : Borislav Mickov : https://github.com/B0k0 ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var mk = moment.defineLocale('mk', { - months : 'јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември'.split('_'), - monthsShort : 'јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек'.split('_'), - weekdays : 'недела_понеделник_вторник_среда_четврток_петок_сабота'.split('_'), - weekdaysShort : 'нед_пон_вто_сре_чет_пет_саб'.split('_'), - weekdaysMin : 'нe_пo_вт_ср_че_пе_сa'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'D.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd, D MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[Денес во] LT', - nextDay : '[Утре во] LT', - nextWeek : '[Во] dddd [во] LT', - lastDay : '[Вчера во] LT', - lastWeek : function () { - switch (this.day()) { +var mk = moment.defineLocale('mk', { + months : 'јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември'.split('_'), + monthsShort : 'јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек'.split('_'), + weekdays : 'недела_понеделник_вторник_среда_четврток_петок_сабота'.split('_'), + weekdaysShort : 'нед_пон_вто_сре_чет_пет_саб'.split('_'), + weekdaysMin : 'нe_пo_вт_ср_че_пе_сa'.split('_'), + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'D.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY H:mm', + LLLL : 'dddd, D MMMM YYYY H:mm' + }, + calendar : { + sameDay : '[Денес во] LT', + nextDay : '[Утре во] LT', + nextWeek : '[Во] dddd [во] LT', + lastDay : '[Вчера во] LT', + lastWeek : function () { + switch (this.day()) { case 0: case 3: case 6: @@ -40,51 +40,51 @@ case 4: case 5: return '[Изминатиот] dddd [во] LT'; - } - }, - sameElse : 'L' - }, - relativeTime : { - future : 'после %s', - past : 'пред %s', - s : 'неколку секунди', - m : 'минута', - mm : '%d минути', - h : 'час', - hh : '%d часа', - d : 'ден', - dd : '%d дена', - M : 'месец', - MM : '%d месеци', - y : 'година', - yy : '%d години' - }, - ordinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/, - ordinal : function (number) { - var lastDigit = number % 10, - last2Digits = number % 100; - if (number === 0) { - return number + '-ев'; - } else if (last2Digits === 0) { - return number + '-ен'; - } else if (last2Digits > 10 && last2Digits < 20) { - return number + '-ти'; - } else if (lastDigit === 1) { - return number + '-ви'; - } else if (lastDigit === 2) { - return number + '-ри'; - } else if (lastDigit === 7 || lastDigit === 8) { - return number + '-ми'; - } else { - return number + '-ти'; } }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. + sameElse : 'L' + }, + relativeTime : { + future : 'после %s', + past : 'пред %s', + s : 'неколку секунди', + m : 'минута', + mm : '%d минути', + h : 'час', + hh : '%d часа', + d : 'ден', + dd : '%d дена', + M : 'месец', + MM : '%d месеци', + y : 'година', + yy : '%d години' + }, + dayOfMonthOrdinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/, + ordinal : function (number) { + var lastDigit = number % 10, + last2Digits = number % 100; + if (number === 0) { + return number + '-ев'; + } else if (last2Digits === 0) { + return number + '-ен'; + } else if (last2Digits > 10 && last2Digits < 20) { + return number + '-ти'; + } else if (lastDigit === 1) { + return number + '-ви'; + } else if (lastDigit === 2) { + return number + '-ри'; + } else if (lastDigit === 7 || lastDigit === 8) { + return number + '-ми'; + } else { + return number + '-ти'; } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return mk; +return mk; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ml.js b/lib/javascripts/moment_locale/ml.js old mode 100755 new mode 100644 index 4ffe9317ce..e5f52e9c9a --- a/lib/javascripts/moment_locale/ml.js +++ b/lib/javascripts/moment_locale/ml.js @@ -1,81 +1,81 @@ //! moment.js locale configuration -//! locale : malayalam (ml) +//! locale : Malayalam [ml] //! author : Floyd Pink : https://github.com/floydpink ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ml = moment.defineLocale('ml', { - months : 'ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ'.split('_'), - monthsShort : 'ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.'.split('_'), - monthsParseExact : true, - weekdays : 'ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച'.split('_'), - weekdaysShort : 'ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി'.split('_'), - weekdaysMin : 'ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ'.split('_'), - longDateFormat : { - LT : 'A h:mm -നു', - LTS : 'A h:mm:ss -നു', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm -നു', - LLLL : 'dddd, D MMMM YYYY, A h:mm -നു' - }, - calendar : { - sameDay : '[ഇന്ന്] LT', - nextDay : '[നാളെ] LT', - nextWeek : 'dddd, LT', - lastDay : '[ഇന്നലെ] LT', - lastWeek : '[കഴിഞ്ഞ] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s കഴിഞ്ഞ്', - past : '%s മുൻപ്', - s : 'അൽപ നിമിഷങ്ങൾ', - m : 'ഒരു മിനിറ്റ്', - mm : '%d മിനിറ്റ്', - h : 'ഒരു മണിക്കൂർ', - hh : '%d മണിക്കൂർ', - d : 'ഒരു ദിവസം', - dd : '%d ദിവസം', - M : 'ഒരു മാസം', - MM : '%d മാസം', - y : 'ഒരു വർഷം', - yy : '%d വർഷം' - }, - meridiemParse: /രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if ((meridiem === 'രാത്രി' && hour >= 4) || - meridiem === 'ഉച്ച കഴിഞ്ഞ്' || - meridiem === 'വൈകുന്നേരം') { - return hour + 12; - } else { - return hour; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'രാത്രി'; - } else if (hour < 12) { - return 'രാവിലെ'; - } else if (hour < 17) { - return 'ഉച്ച കഴിഞ്ഞ്'; - } else if (hour < 20) { - return 'വൈകുന്നേരം'; - } else { - return 'രാത്രി'; - } +var ml = moment.defineLocale('ml', { + months : 'ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ'.split('_'), + monthsShort : 'ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.'.split('_'), + monthsParseExact : true, + weekdays : 'ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച'.split('_'), + weekdaysShort : 'ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി'.split('_'), + weekdaysMin : 'ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ'.split('_'), + longDateFormat : { + LT : 'A h:mm -നു', + LTS : 'A h:mm:ss -നു', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm -നു', + LLLL : 'dddd, D MMMM YYYY, A h:mm -നു' + }, + calendar : { + sameDay : '[ഇന്ന്] LT', + nextDay : '[നാളെ] LT', + nextWeek : 'dddd, LT', + lastDay : '[ഇന്നലെ] LT', + lastWeek : '[കഴിഞ്ഞ] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s കഴിഞ്ഞ്', + past : '%s മുൻപ്', + s : 'അൽപ നിമിഷങ്ങൾ', + m : 'ഒരു മിനിറ്റ്', + mm : '%d മിനിറ്റ്', + h : 'ഒരു മണിക്കൂർ', + hh : '%d മണിക്കൂർ', + d : 'ഒരു ദിവസം', + dd : '%d ദിവസം', + M : 'ഒരു മാസം', + MM : '%d മാസം', + y : 'ഒരു വർഷം', + yy : '%d വർഷം' + }, + meridiemParse: /രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if ((meridiem === 'രാത്രി' && hour >= 4) || + meridiem === 'ഉച്ച കഴിഞ്ഞ്' || + meridiem === 'വൈകുന്നേരം') { + return hour + 12; + } else { + return hour; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'രാത്രി'; + } else if (hour < 12) { + return 'രാവിലെ'; + } else if (hour < 17) { + return 'ഉച്ച കഴിഞ്ഞ്'; + } else if (hour < 20) { + return 'വൈകുന്നേരം'; + } else { + return 'രാത്രി'; + } + } +}); - return ml; +return ml; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/mr.js b/lib/javascripts/moment_locale/mr.js old mode 100755 new mode 100644 index 2bdf262dfb..abe1024b13 --- a/lib/javascripts/moment_locale/mr.js +++ b/lib/javascripts/moment_locale/mr.js @@ -1,159 +1,159 @@ //! moment.js locale configuration -//! locale : Marathi (mr) +//! locale : Marathi [mr] //! author : Harshad Kale : https://github.com/kalehv //! author : Vivek Athalye : https://github.com/vnathalye ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '१', - '2': '२', - '3': '३', - '4': '४', - '5': '५', - '6': '६', - '7': '७', - '8': '८', - '9': '९', - '0': '०' - }, - numberMap = { - '१': '1', - '२': '2', - '३': '3', - '४': '4', - '५': '5', - '६': '6', - '७': '7', - '८': '8', - '९': '9', - '०': '0' - }; +var symbolMap = { + '1': '१', + '2': '२', + '3': '३', + '4': '४', + '5': '५', + '6': '६', + '7': '७', + '8': '८', + '9': '९', + '0': '०' +}; +var numberMap = { + '१': '1', + '२': '2', + '३': '3', + '४': '4', + '५': '5', + '६': '6', + '७': '7', + '८': '8', + '९': '9', + '०': '0' +}; - function relativeTimeMr(number, withoutSuffix, string, isFuture) - { - var output = ''; - if (withoutSuffix) { - switch (string) { - case 's': output = 'काही सेकंद'; break; - case 'm': output = 'एक मिनिट'; break; - case 'mm': output = '%d मिनिटे'; break; - case 'h': output = 'एक तास'; break; - case 'hh': output = '%d तास'; break; - case 'd': output = 'एक दिवस'; break; - case 'dd': output = '%d दिवस'; break; - case 'M': output = 'एक महिना'; break; - case 'MM': output = '%d महिने'; break; - case 'y': output = 'एक वर्ष'; break; - case 'yy': output = '%d वर्षे'; break; - } +function relativeTimeMr(number, withoutSuffix, string, isFuture) +{ + var output = ''; + if (withoutSuffix) { + switch (string) { + case 's': output = 'काही सेकंद'; break; + case 'm': output = 'एक मिनिट'; break; + case 'mm': output = '%d मिनिटे'; break; + case 'h': output = 'एक तास'; break; + case 'hh': output = '%d तास'; break; + case 'd': output = 'एक दिवस'; break; + case 'dd': output = '%d दिवस'; break; + case 'M': output = 'एक महिना'; break; + case 'MM': output = '%d महिने'; break; + case 'y': output = 'एक वर्ष'; break; + case 'yy': output = '%d वर्षे'; break; } - else { - switch (string) { - case 's': output = 'काही सेकंदां'; break; - case 'm': output = 'एका मिनिटा'; break; - case 'mm': output = '%d मिनिटां'; break; - case 'h': output = 'एका तासा'; break; - case 'hh': output = '%d तासां'; break; - case 'd': output = 'एका दिवसा'; break; - case 'dd': output = '%d दिवसां'; break; - case 'M': output = 'एका महिन्या'; break; - case 'MM': output = '%d महिन्यां'; break; - case 'y': output = 'एका वर्षा'; break; - case 'yy': output = '%d वर्षां'; break; - } - } - return output.replace(/%d/i, number); } - - var mr = moment.defineLocale('mr', { - months : 'जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर'.split('_'), - monthsShort: 'जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.'.split('_'), - monthsParseExact : true, - weekdays : 'रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'), - weekdaysShort : 'रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि'.split('_'), - weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'), - longDateFormat : { - LT : 'A h:mm वाजता', - LTS : 'A h:mm:ss वाजता', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm वाजता', - LLLL : 'dddd, D MMMM YYYY, A h:mm वाजता' - }, - calendar : { - sameDay : '[आज] LT', - nextDay : '[उद्या] LT', - nextWeek : 'dddd, LT', - lastDay : '[काल] LT', - lastWeek: '[मागील] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future: '%sमध्ये', - past: '%sपूर्वी', - s: relativeTimeMr, - m: relativeTimeMr, - mm: relativeTimeMr, - h: relativeTimeMr, - hh: relativeTimeMr, - d: relativeTimeMr, - dd: relativeTimeMr, - M: relativeTimeMr, - MM: relativeTimeMr, - y: relativeTimeMr, - yy: relativeTimeMr - }, - preparse: function (string) { - return string.replace(/[१२३४५६७८९०]/g, function (match) { - return numberMap[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }); - }, - meridiemParse: /रात्री|सकाळी|दुपारी|सायंकाळी/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'रात्री') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'सकाळी') { - return hour; - } else if (meridiem === 'दुपारी') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'सायंकाळी') { - return hour + 12; - } - }, - meridiem: function (hour, minute, isLower) { - if (hour < 4) { - return 'रात्री'; - } else if (hour < 10) { - return 'सकाळी'; - } else if (hour < 17) { - return 'दुपारी'; - } else if (hour < 20) { - return 'सायंकाळी'; - } else { - return 'रात्री'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. + else { + switch (string) { + case 's': output = 'काही सेकंदां'; break; + case 'm': output = 'एका मिनिटा'; break; + case 'mm': output = '%d मिनिटां'; break; + case 'h': output = 'एका तासा'; break; + case 'hh': output = '%d तासां'; break; + case 'd': output = 'एका दिवसा'; break; + case 'dd': output = '%d दिवसां'; break; + case 'M': output = 'एका महिन्या'; break; + case 'MM': output = '%d महिन्यां'; break; + case 'y': output = 'एका वर्षा'; break; + case 'yy': output = '%d वर्षां'; break; } - }); + } + return output.replace(/%d/i, number); +} - return mr; +var mr = moment.defineLocale('mr', { + months : 'जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर'.split('_'), + monthsShort: 'जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.'.split('_'), + monthsParseExact : true, + weekdays : 'रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'), + weekdaysShort : 'रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि'.split('_'), + weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'), + longDateFormat : { + LT : 'A h:mm वाजता', + LTS : 'A h:mm:ss वाजता', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm वाजता', + LLLL : 'dddd, D MMMM YYYY, A h:mm वाजता' + }, + calendar : { + sameDay : '[आज] LT', + nextDay : '[उद्या] LT', + nextWeek : 'dddd, LT', + lastDay : '[काल] LT', + lastWeek: '[मागील] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future: '%sमध्ये', + past: '%sपूर्वी', + s: relativeTimeMr, + m: relativeTimeMr, + mm: relativeTimeMr, + h: relativeTimeMr, + hh: relativeTimeMr, + d: relativeTimeMr, + dd: relativeTimeMr, + M: relativeTimeMr, + MM: relativeTimeMr, + y: relativeTimeMr, + yy: relativeTimeMr + }, + preparse: function (string) { + return string.replace(/[१२३४५६७८९०]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + meridiemParse: /रात्री|सकाळी|दुपारी|सायंकाळी/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === 'रात्री') { + return hour < 4 ? hour : hour + 12; + } else if (meridiem === 'सकाळी') { + return hour; + } else if (meridiem === 'दुपारी') { + return hour >= 10 ? hour : hour + 12; + } else if (meridiem === 'सायंकाळी') { + return hour + 12; + } + }, + meridiem: function (hour, minute, isLower) { + if (hour < 4) { + return 'रात्री'; + } else if (hour < 10) { + return 'सकाळी'; + } else if (hour < 17) { + return 'दुपारी'; + } else if (hour < 20) { + return 'सायंकाळी'; + } else { + return 'रात्री'; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); -})); \ No newline at end of file +return mr; + +}))); diff --git a/lib/javascripts/moment_locale/ms-my.js b/lib/javascripts/moment_locale/ms-my.js old mode 100755 new mode 100644 index 38c3138780..0cb403d347 --- a/lib/javascripts/moment_locale/ms-my.js +++ b/lib/javascripts/moment_locale/ms-my.js @@ -1,82 +1,83 @@ //! moment.js locale configuration -//! locale : Bahasa Malaysia (ms-MY) +//! locale : Malay [ms-my] +//! note : DEPRECATED, the correct one is [ms] //! author : Weldan Jamili : https://github.com/weldan ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ms_my = moment.defineLocale('ms-my', { - months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'), - monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'), - weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'), - weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'), - weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /pagi|tengahari|petang|malam/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'pagi') { - return hour; - } else if (meridiem === 'tengahari') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'petang' || meridiem === 'malam') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'pagi'; - } else if (hours < 15) { - return 'tengahari'; - } else if (hours < 19) { - return 'petang'; - } else { - return 'malam'; - } - }, - calendar : { - sameDay : '[Hari ini pukul] LT', - nextDay : '[Esok pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kelmarin pukul] LT', - lastWeek : 'dddd [lepas pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dalam %s', - past : '%s yang lepas', - s : 'beberapa saat', - m : 'seminit', - mm : '%d minit', - h : 'sejam', - hh : '%d jam', - d : 'sehari', - dd : '%d hari', - M : 'sebulan', - MM : '%d bulan', - y : 'setahun', - yy : '%d tahun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var msMy = moment.defineLocale('ms-my', { + months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'), + monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'), + weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'), + weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'), + weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'), + longDateFormat : { + LT : 'HH.mm', + LTS : 'HH.mm.ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY [pukul] HH.mm', + LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' + }, + meridiemParse: /pagi|tengahari|petang|malam/, + meridiemHour: function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if (meridiem === 'pagi') { + return hour; + } else if (meridiem === 'tengahari') { + return hour >= 11 ? hour : hour + 12; + } else if (meridiem === 'petang' || meridiem === 'malam') { + return hour + 12; + } + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 11) { + return 'pagi'; + } else if (hours < 15) { + return 'tengahari'; + } else if (hours < 19) { + return 'petang'; + } else { + return 'malam'; + } + }, + calendar : { + sameDay : '[Hari ini pukul] LT', + nextDay : '[Esok pukul] LT', + nextWeek : 'dddd [pukul] LT', + lastDay : '[Kelmarin pukul] LT', + lastWeek : 'dddd [lepas pukul] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'dalam %s', + past : '%s yang lepas', + s : 'beberapa saat', + m : 'seminit', + mm : '%d minit', + h : 'sejam', + hh : '%d jam', + d : 'sehari', + dd : '%d hari', + M : 'sebulan', + MM : '%d bulan', + y : 'setahun', + yy : '%d tahun' + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return ms_my; +return msMy; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ms.js b/lib/javascripts/moment_locale/ms.js old mode 100755 new mode 100644 index fbbb734d1d..4d4afff5df --- a/lib/javascripts/moment_locale/ms.js +++ b/lib/javascripts/moment_locale/ms.js @@ -1,82 +1,82 @@ //! moment.js locale configuration -//! locale : Bahasa Malaysia (ms-MY) +//! locale : Malay [ms] //! author : Weldan Jamili : https://github.com/weldan ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var ms = moment.defineLocale('ms', { - months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'), - monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'), - weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'), - weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'), - weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /pagi|tengahari|petang|malam/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'pagi') { - return hour; - } else if (meridiem === 'tengahari') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'petang' || meridiem === 'malam') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'pagi'; - } else if (hours < 15) { - return 'tengahari'; - } else if (hours < 19) { - return 'petang'; - } else { - return 'malam'; - } - }, - calendar : { - sameDay : '[Hari ini pukul] LT', - nextDay : '[Esok pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kelmarin pukul] LT', - lastWeek : 'dddd [lepas pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dalam %s', - past : '%s yang lepas', - s : 'beberapa saat', - m : 'seminit', - mm : '%d minit', - h : 'sejam', - hh : '%d jam', - d : 'sehari', - dd : '%d hari', - M : 'sebulan', - MM : '%d bulan', - y : 'setahun', - yy : '%d tahun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var ms = moment.defineLocale('ms', { + months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'), + monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'), + weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'), + weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'), + weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'), + longDateFormat : { + LT : 'HH.mm', + LTS : 'HH.mm.ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY [pukul] HH.mm', + LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' + }, + meridiemParse: /pagi|tengahari|petang|malam/, + meridiemHour: function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if (meridiem === 'pagi') { + return hour; + } else if (meridiem === 'tengahari') { + return hour >= 11 ? hour : hour + 12; + } else if (meridiem === 'petang' || meridiem === 'malam') { + return hour + 12; + } + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 11) { + return 'pagi'; + } else if (hours < 15) { + return 'tengahari'; + } else if (hours < 19) { + return 'petang'; + } else { + return 'malam'; + } + }, + calendar : { + sameDay : '[Hari ini pukul] LT', + nextDay : '[Esok pukul] LT', + nextWeek : 'dddd [pukul] LT', + lastDay : '[Kelmarin pukul] LT', + lastWeek : 'dddd [lepas pukul] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'dalam %s', + past : '%s yang lepas', + s : 'beberapa saat', + m : 'seminit', + mm : '%d minit', + h : 'sejam', + hh : '%d jam', + d : 'sehari', + dd : '%d hari', + M : 'sebulan', + MM : '%d bulan', + y : 'setahun', + yy : '%d tahun' + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return ms; +return ms; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/my.js b/lib/javascripts/moment_locale/my.js old mode 100755 new mode 100644 index 71e99f0ed8..32d67e2bae --- a/lib/javascripts/moment_locale/my.js +++ b/lib/javascripts/moment_locale/my.js @@ -1,93 +1,96 @@ //! moment.js locale configuration -//! locale : Burmese (my) +//! locale : Burmese [my] //! author : Squar team, mysquar.com +//! author : David Rossellat : https://github.com/gholadr +//! author : Tin Aung Lin : https://github.com/thanyawzinmin ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '၁', - '2': '၂', - '3': '၃', - '4': '၄', - '5': '၅', - '6': '၆', - '7': '၇', - '8': '၈', - '9': '၉', - '0': '၀' - }, numberMap = { - '၁': '1', - '၂': '2', - '၃': '3', - '၄': '4', - '၅': '5', - '၆': '6', - '၇': '7', - '၈': '8', - '၉': '9', - '၀': '0' - }; +var symbolMap = { + '1': '၁', + '2': '၂', + '3': '၃', + '4': '၄', + '5': '၅', + '6': '၆', + '7': '၇', + '8': '၈', + '9': '၉', + '0': '၀' +}; +var numberMap = { + '၁': '1', + '၂': '2', + '၃': '3', + '၄': '4', + '၅': '5', + '၆': '6', + '၇': '7', + '၈': '8', + '၉': '9', + '၀': '0' +}; - var my = moment.defineLocale('my', { - months: 'ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ'.split('_'), - monthsShort: 'ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ'.split('_'), - weekdays: 'တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ'.split('_'), - weekdaysShort: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'), - weekdaysMin: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'), +var my = moment.defineLocale('my', { + months: 'ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ'.split('_'), + monthsShort: 'ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ'.split('_'), + weekdays: 'တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ'.split('_'), + weekdaysShort: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'), + weekdaysMin: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'), - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd D MMMM YYYY HH:mm' - }, - calendar: { - sameDay: '[ယနေ.] LT [မှာ]', - nextDay: '[မနက်ဖြန်] LT [မှာ]', - nextWeek: 'dddd LT [မှာ]', - lastDay: '[မနေ.က] LT [မှာ]', - lastWeek: '[ပြီးခဲ့သော] dddd LT [မှာ]', - sameElse: 'L' - }, - relativeTime: { - future: 'လာမည့် %s မှာ', - past: 'လွန်ခဲ့သော %s က', - s: 'စက္ကန်.အနည်းငယ်', - m: 'တစ်မိနစ်', - mm: '%d မိနစ်', - h: 'တစ်နာရီ', - hh: '%d နာရီ', - d: 'တစ်ရက်', - dd: '%d ရက်', - M: 'တစ်လ', - MM: '%d လ', - y: 'တစ်နှစ်', - yy: '%d နှစ်' - }, - preparse: function (string) { - return string.replace(/[၁၂၃၄၅၆၇၈၉၀]/g, function (match) { - return numberMap[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }); - }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 1st is the first week of the year. - } - }); + longDateFormat: { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L: 'DD/MM/YYYY', + LL: 'D MMMM YYYY', + LLL: 'D MMMM YYYY HH:mm', + LLLL: 'dddd D MMMM YYYY HH:mm' + }, + calendar: { + sameDay: '[ယနေ.] LT [မှာ]', + nextDay: '[မနက်ဖြန်] LT [မှာ]', + nextWeek: 'dddd LT [မှာ]', + lastDay: '[မနေ.က] LT [မှာ]', + lastWeek: '[ပြီးခဲ့သော] dddd LT [မှာ]', + sameElse: 'L' + }, + relativeTime: { + future: 'လာမည့် %s မှာ', + past: 'လွန်ခဲ့သော %s က', + s: 'စက္ကန်.အနည်းငယ်', + m: 'တစ်မိနစ်', + mm: '%d မိနစ်', + h: 'တစ်နာရီ', + hh: '%d နာရီ', + d: 'တစ်ရက်', + dd: '%d ရက်', + M: 'တစ်လ', + MM: '%d လ', + y: 'တစ်နှစ်', + yy: '%d နှစ်' + }, + preparse: function (string) { + return string.replace(/[၁၂၃၄၅၆၇၈၉၀]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 1st is the first week of the year. + } +}); - return my; +return my; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/nb.js b/lib/javascripts/moment_locale/nb.js old mode 100755 new mode 100644 index 45b57a1df3..2cfe374f84 --- a/lib/javascripts/moment_locale/nb.js +++ b/lib/javascripts/moment_locale/nb.js @@ -1,63 +1,63 @@ //! moment.js locale configuration -//! locale : norwegian bokmål (nb) +//! locale : Norwegian Bokmål [nb] //! authors : Espen Hovlandsdal : https://github.com/rexxars //! Sigurd Gartmann : https://github.com/sigurdga ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var nb = moment.defineLocale('nb', { - months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'), - monthsShort : 'jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.'.split('_'), - monthsParseExact : true, - weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'), - weekdaysShort : 'sø._ma._ti._on._to._fr._lø.'.split('_'), - weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY [kl.] HH:mm', - LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm' - }, - calendar : { - sameDay: '[i dag kl.] LT', - nextDay: '[i morgen kl.] LT', - nextWeek: 'dddd [kl.] LT', - lastDay: '[i går kl.] LT', - lastWeek: '[forrige] dddd [kl.] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'om %s', - past : '%s siden', - s : 'noen sekunder', - m : 'ett minutt', - mm : '%d minutter', - h : 'en time', - hh : '%d timer', - d : 'en dag', - dd : '%d dager', - M : 'en måned', - MM : '%d måneder', - y : 'ett år', - yy : '%d år' - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var nb = moment.defineLocale('nb', { + months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'), + monthsShort : 'jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.'.split('_'), + monthsParseExact : true, + weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'), + weekdaysShort : 'sø._ma._ti._on._to._fr._lø.'.split('_'), + weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY [kl.] HH:mm', + LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm' + }, + calendar : { + sameDay: '[i dag kl.] LT', + nextDay: '[i morgen kl.] LT', + nextWeek: 'dddd [kl.] LT', + lastDay: '[i går kl.] LT', + lastWeek: '[forrige] dddd [kl.] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'om %s', + past : '%s siden', + s : 'noen sekunder', + m : 'ett minutt', + mm : '%d minutter', + h : 'en time', + hh : '%d timer', + d : 'en dag', + dd : '%d dager', + M : 'en måned', + MM : '%d måneder', + y : 'ett år', + yy : '%d år' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return nb; +return nb; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ne.js b/lib/javascripts/moment_locale/ne.js old mode 100755 new mode 100644 index f40eb8d516..a35947a9e3 --- a/lib/javascripts/moment_locale/ne.js +++ b/lib/javascripts/moment_locale/ne.js @@ -1,123 +1,123 @@ //! moment.js locale configuration -//! locale : nepali/nepalese +//! locale : Nepalese [ne] //! author : suvash : https://github.com/suvash ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '१', - '2': '२', - '3': '३', - '4': '४', - '5': '५', - '6': '६', - '7': '७', - '8': '८', - '9': '९', - '0': '०' +var symbolMap = { + '1': '१', + '2': '२', + '3': '३', + '4': '४', + '5': '५', + '6': '६', + '7': '७', + '8': '८', + '9': '९', + '0': '०' +}; +var numberMap = { + '१': '1', + '२': '2', + '३': '3', + '४': '4', + '५': '5', + '६': '6', + '७': '7', + '८': '8', + '९': '9', + '०': '0' +}; + +var ne = moment.defineLocale('ne', { + months : 'जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर'.split('_'), + monthsShort : 'जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.'.split('_'), + monthsParseExact : true, + weekdays : 'आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार'.split('_'), + weekdaysShort : 'आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.'.split('_'), + weekdaysMin : 'आ._सो._मं._बु._बि._शु._श.'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'Aको h:mm बजे', + LTS : 'Aको h:mm:ss बजे', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, Aको h:mm बजे', + LLLL : 'dddd, D MMMM YYYY, Aको h:mm बजे' }, - numberMap = { - '१': '1', - '२': '2', - '३': '3', - '४': '4', - '५': '5', - '६': '6', - '७': '7', - '८': '8', - '९': '9', - '०': '0' - }; - - var ne = moment.defineLocale('ne', { - months : 'जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर'.split('_'), - monthsShort : 'जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.'.split('_'), - monthsParseExact : true, - weekdays : 'आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार'.split('_'), - weekdaysShort : 'आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.'.split('_'), - weekdaysMin : 'आ._सो._मं._बु._बि._शु._श.'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'Aको h:mm बजे', - LTS : 'Aको h:mm:ss बजे', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, Aको h:mm बजे', - LLLL : 'dddd, D MMMM YYYY, Aको h:mm बजे' - }, - preparse: function (string) { - return string.replace(/[१२३४५६७८९०]/g, function (match) { - return numberMap[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }); - }, - meridiemParse: /राति|बिहान|दिउँसो|साँझ/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'राति') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'बिहान') { - return hour; - } else if (meridiem === 'दिउँसो') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'साँझ') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 3) { - return 'राति'; - } else if (hour < 12) { - return 'बिहान'; - } else if (hour < 16) { - return 'दिउँसो'; - } else if (hour < 20) { - return 'साँझ'; - } else { - return 'राति'; - } - }, - calendar : { - sameDay : '[आज] LT', - nextDay : '[भोलि] LT', - nextWeek : '[आउँदो] dddd[,] LT', - lastDay : '[हिजो] LT', - lastWeek : '[गएको] dddd[,] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%sमा', - past : '%s अगाडि', - s : 'केही क्षण', - m : 'एक मिनेट', - mm : '%d मिनेट', - h : 'एक घण्टा', - hh : '%d घण्टा', - d : 'एक दिन', - dd : '%d दिन', - M : 'एक महिना', - MM : '%d महिना', - y : 'एक बर्ष', - yy : '%d बर्ष' - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. + preparse: function (string) { + return string.replace(/[१२३४५६७८९०]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + meridiemParse: /राति|बिहान|दिउँसो|साँझ/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if (meridiem === 'राति') { + return hour < 4 ? hour : hour + 12; + } else if (meridiem === 'बिहान') { + return hour; + } else if (meridiem === 'दिउँसो') { + return hour >= 10 ? hour : hour + 12; + } else if (meridiem === 'साँझ') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 3) { + return 'राति'; + } else if (hour < 12) { + return 'बिहान'; + } else if (hour < 16) { + return 'दिउँसो'; + } else if (hour < 20) { + return 'साँझ'; + } else { + return 'राति'; + } + }, + calendar : { + sameDay : '[आज] LT', + nextDay : '[भोलि] LT', + nextWeek : '[आउँदो] dddd[,] LT', + lastDay : '[हिजो] LT', + lastWeek : '[गएको] dddd[,] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%sमा', + past : '%s अगाडि', + s : 'केही क्षण', + m : 'एक मिनेट', + mm : '%d मिनेट', + h : 'एक घण्टा', + hh : '%d घण्टा', + d : 'एक दिन', + dd : '%d दिन', + M : 'एक महिना', + MM : '%d महिना', + y : 'एक बर्ष', + yy : '%d बर्ष' + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); - return ne; +return ne; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/nl-be.js b/lib/javascripts/moment_locale/nl-be.js new file mode 100644 index 0000000000..8588eab402 --- /dev/null +++ b/lib/javascripts/moment_locale/nl-be.js @@ -0,0 +1,88 @@ +//! moment.js locale configuration +//! locale : Dutch (Belgium) [nl-be] +//! author : Joris Röling : https://github.com/jorisroling +//! author : Jacob Middag : https://github.com/middagj + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var monthsShortWithDots = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_'); +var monthsShortWithoutDots = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_'); + +var monthsParse = [/^jan/i, /^feb/i, /^maart|mrt.?$/i, /^apr/i, /^mei$/i, /^jun[i.]?$/i, /^jul[i.]?$/i, /^aug/i, /^sep/i, /^okt/i, /^nov/i, /^dec/i]; +var monthsRegex = /^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i; + +var nlBe = moment.defineLocale('nl-be', { + months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'), + monthsShort : function (m, format) { + if (!m) { + return monthsShortWithDots; + } else if (/-MMM-/.test(format)) { + return monthsShortWithoutDots[m.month()]; + } else { + return monthsShortWithDots[m.month()]; + } + }, + + monthsRegex: monthsRegex, + monthsShortRegex: monthsRegex, + monthsStrictRegex: /^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i, + monthsShortStrictRegex: /^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i, + + monthsParse : monthsParse, + longMonthsParse : monthsParse, + shortMonthsParse : monthsParse, + + weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'), + weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'), + weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[vandaag om] LT', + nextDay: '[morgen om] LT', + nextWeek: 'dddd [om] LT', + lastDay: '[gisteren om] LT', + lastWeek: '[afgelopen] dddd [om] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'over %s', + past : '%s geleden', + s : 'een paar seconden', + m : 'één minuut', + mm : '%d minuten', + h : 'één uur', + hh : '%d uur', + d : 'één dag', + dd : '%d dagen', + M : 'één maand', + MM : '%d maanden', + y : 'één jaar', + yy : '%d jaar' + }, + dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, + ordinal : function (number) { + return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return nlBe; + +}))); diff --git a/lib/javascripts/moment_locale/nl.js b/lib/javascripts/moment_locale/nl.js old mode 100755 new mode 100644 index df36a45c62..2863b08c74 --- a/lib/javascripts/moment_locale/nl.js +++ b/lib/javascripts/moment_locale/nl.js @@ -1,73 +1,88 @@ //! moment.js locale configuration -//! locale : dutch (nl) -//! author : Joris Röling : https://github.com/jjupiter +//! locale : Dutch [nl] +//! author : Joris Röling : https://github.com/jorisroling +//! author : Jacob Middag : https://github.com/middagj ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var monthsShortWithDots = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_'), - monthsShortWithoutDots = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_'); +var monthsShortWithDots = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_'); +var monthsShortWithoutDots = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_'); - var nl = moment.defineLocale('nl', { - months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'), - monthsShort : function (m, format) { - if (/-MMM-/.test(format)) { - return monthsShortWithoutDots[m.month()]; - } else { - return monthsShortWithDots[m.month()]; - } - }, - monthsParseExact : true, - weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'), - weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'), - weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[vandaag om] LT', - nextDay: '[morgen om] LT', - nextWeek: 'dddd [om] LT', - lastDay: '[gisteren om] LT', - lastWeek: '[afgelopen] dddd [om] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'over %s', - past : '%s geleden', - s : 'een paar seconden', - m : 'één minuut', - mm : '%d minuten', - h : 'één uur', - hh : '%d uur', - d : 'één dag', - dd : '%d dagen', - M : 'één maand', - MM : '%d maanden', - y : 'één jaar', - yy : '%d jaar' - }, - ordinalParse: /\d{1,2}(ste|de)/, - ordinal : function (number) { - return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. +var monthsParse = [/^jan/i, /^feb/i, /^maart|mrt.?$/i, /^apr/i, /^mei$/i, /^jun[i.]?$/i, /^jul[i.]?$/i, /^aug/i, /^sep/i, /^okt/i, /^nov/i, /^dec/i]; +var monthsRegex = /^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i; + +var nl = moment.defineLocale('nl', { + months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'), + monthsShort : function (m, format) { + if (!m) { + return monthsShortWithDots; + } else if (/-MMM-/.test(format)) { + return monthsShortWithoutDots[m.month()]; + } else { + return monthsShortWithDots[m.month()]; } - }); + }, - return nl; + monthsRegex: monthsRegex, + monthsShortRegex: monthsRegex, + monthsStrictRegex: /^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i, + monthsShortStrictRegex: /^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i, -})); \ No newline at end of file + monthsParse : monthsParse, + longMonthsParse : monthsParse, + shortMonthsParse : monthsParse, + + weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'), + weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'), + weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD-MM-YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[vandaag om] LT', + nextDay: '[morgen om] LT', + nextWeek: 'dddd [om] LT', + lastDay: '[gisteren om] LT', + lastWeek: '[afgelopen] dddd [om] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'over %s', + past : '%s geleden', + s : 'een paar seconden', + m : 'één minuut', + mm : '%d minuten', + h : 'één uur', + hh : '%d uur', + d : 'één dag', + dd : '%d dagen', + M : 'één maand', + MM : '%d maanden', + y : 'één jaar', + yy : '%d jaar' + }, + dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, + ordinal : function (number) { + return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return nl; + +}))); diff --git a/lib/javascripts/moment_locale/nn.js b/lib/javascripts/moment_locale/nn.js old mode 100755 new mode 100644 index 980b530565..90dda8c6c5 --- a/lib/javascripts/moment_locale/nn.js +++ b/lib/javascripts/moment_locale/nn.js @@ -1,60 +1,60 @@ //! moment.js locale configuration -//! locale : norwegian nynorsk (nn) +//! locale : Nynorsk [nn] //! author : https://github.com/mechuwind ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var nn = moment.defineLocale('nn', { - months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'), - monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'), - weekdays : 'sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag'.split('_'), - weekdaysShort : 'sun_mån_tys_ons_tor_fre_lau'.split('_'), - weekdaysMin : 'su_må_ty_on_to_fr_lø'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY [kl.] H:mm', - LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm' - }, - calendar : { - sameDay: '[I dag klokka] LT', - nextDay: '[I morgon klokka] LT', - nextWeek: 'dddd [klokka] LT', - lastDay: '[I går klokka] LT', - lastWeek: '[Føregåande] dddd [klokka] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'om %s', - past : '%s sidan', - s : 'nokre sekund', - m : 'eit minutt', - mm : '%d minutt', - h : 'ein time', - hh : '%d timar', - d : 'ein dag', - dd : '%d dagar', - M : 'ein månad', - MM : '%d månader', - y : 'eit år', - yy : '%d år' - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var nn = moment.defineLocale('nn', { + months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'), + monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'), + weekdays : 'sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag'.split('_'), + weekdaysShort : 'sun_mån_tys_ons_tor_fre_lau'.split('_'), + weekdaysMin : 'su_må_ty_on_to_fr_lø'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY [kl.] H:mm', + LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm' + }, + calendar : { + sameDay: '[I dag klokka] LT', + nextDay: '[I morgon klokka] LT', + nextWeek: 'dddd [klokka] LT', + lastDay: '[I går klokka] LT', + lastWeek: '[Føregåande] dddd [klokka] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'om %s', + past : '%s sidan', + s : 'nokre sekund', + m : 'eit minutt', + mm : '%d minutt', + h : 'ein time', + hh : '%d timar', + d : 'ein dag', + dd : '%d dagar', + M : 'ein månad', + MM : '%d månader', + y : 'eit år', + yy : '%d år' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return nn; +return nn; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/pa-in.js b/lib/javascripts/moment_locale/pa-in.js new file mode 100644 index 0000000000..6dec8d1724 --- /dev/null +++ b/lib/javascripts/moment_locale/pa-in.js @@ -0,0 +1,124 @@ +//! moment.js locale configuration +//! locale : Punjabi (India) [pa-in] +//! author : Harpreet Singh : https://github.com/harpreetkhalsagtbit + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var symbolMap = { + '1': '੧', + '2': '੨', + '3': '੩', + '4': '੪', + '5': '੫', + '6': '੬', + '7': '੭', + '8': '੮', + '9': '੯', + '0': '੦' +}; +var numberMap = { + '੧': '1', + '੨': '2', + '੩': '3', + '੪': '4', + '੫': '5', + '੬': '6', + '੭': '7', + '੮': '8', + '੯': '9', + '੦': '0' +}; + +var paIn = moment.defineLocale('pa-in', { + // There are months name as per Nanakshahi Calender but they are not used as rigidly in modern Punjabi. + months : 'ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ'.split('_'), + monthsShort : 'ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ'.split('_'), + weekdays : 'ਐਤਵਾਰ_ਸੋਮਵਾਰ_ਮੰਗਲਵਾਰ_ਬੁਧਵਾਰ_ਵੀਰਵਾਰ_ਸ਼ੁੱਕਰਵਾਰ_ਸ਼ਨੀਚਰਵਾਰ'.split('_'), + weekdaysShort : 'ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ'.split('_'), + weekdaysMin : 'ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ'.split('_'), + longDateFormat : { + LT : 'A h:mm ਵਜੇ', + LTS : 'A h:mm:ss ਵਜੇ', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm ਵਜੇ', + LLLL : 'dddd, D MMMM YYYY, A h:mm ਵਜੇ' + }, + calendar : { + sameDay : '[ਅਜ] LT', + nextDay : '[ਕਲ] LT', + nextWeek : 'dddd, LT', + lastDay : '[ਕਲ] LT', + lastWeek : '[ਪਿਛਲੇ] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s ਵਿੱਚ', + past : '%s ਪਿਛਲੇ', + s : 'ਕੁਝ ਸਕਿੰਟ', + m : 'ਇਕ ਮਿੰਟ', + mm : '%d ਮਿੰਟ', + h : 'ਇੱਕ ਘੰਟਾ', + hh : '%d ਘੰਟੇ', + d : 'ਇੱਕ ਦਿਨ', + dd : '%d ਦਿਨ', + M : 'ਇੱਕ ਮਹੀਨਾ', + MM : '%d ਮਹੀਨੇ', + y : 'ਇੱਕ ਸਾਲ', + yy : '%d ਸਾਲ' + }, + preparse: function (string) { + return string.replace(/[੧੨੩੪੫੬੭੮੯੦]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + // Punjabi notation for meridiems are quite fuzzy in practice. While there exists + // a rigid notion of a 'Pahar' it is not used as rigidly in modern Punjabi. + meridiemParse: /ਰਾਤ|ਸਵੇਰ|ਦੁਪਹਿਰ|ਸ਼ਾਮ/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === 'ਰਾਤ') { + return hour < 4 ? hour : hour + 12; + } else if (meridiem === 'ਸਵੇਰ') { + return hour; + } else if (meridiem === 'ਦੁਪਹਿਰ') { + return hour >= 10 ? hour : hour + 12; + } else if (meridiem === 'ਸ਼ਾਮ') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'ਰਾਤ'; + } else if (hour < 10) { + return 'ਸਵੇਰ'; + } else if (hour < 17) { + return 'ਦੁਪਹਿਰ'; + } else if (hour < 20) { + return 'ਸ਼ਾਮ'; + } else { + return 'ਰਾਤ'; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); + +return paIn; + +}))); diff --git a/lib/javascripts/moment_locale/pl.js b/lib/javascripts/moment_locale/pl.js old mode 100755 new mode 100644 index fa8157943b..af2c331f22 --- a/lib/javascripts/moment_locale/pl.js +++ b/lib/javascripts/moment_locale/pl.js @@ -1,23 +1,23 @@ //! moment.js locale configuration -//! locale : polish (pl) +//! locale : Polish [pl] //! author : Rafal Hirsz : https://github.com/evoL ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var monthsNominative = 'styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień'.split('_'), - monthsSubjective = 'stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia'.split('_'); - function plural(n) { - return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1); - } - function translate(number, withoutSuffix, key) { - var result = number + ' '; - switch (key) { +var monthsNominative = 'styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień'.split('_'); +var monthsSubjective = 'stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia'.split('_'); +function plural(n) { + return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1); +} +function translate(number, withoutSuffix, key) { + var result = number + ' '; + switch (key) { case 'm': return withoutSuffix ? 'minuta' : 'minutę'; case 'mm': @@ -30,41 +30,43 @@ return result + (plural(number) ? 'miesiące' : 'miesięcy'); case 'yy': return result + (plural(number) ? 'lata' : 'lat'); - } } +} - var pl = moment.defineLocale('pl', { - months : function (momentToFormat, format) { - if (format === '') { - // Hack: if format empty we know this is used to generate - // RegExp by moment. Give then back both valid forms of months - // in RegExp ready format. - return '(' + monthsSubjective[momentToFormat.month()] + '|' + monthsNominative[momentToFormat.month()] + ')'; - } else if (/D MMMM/.test(format)) { - return monthsSubjective[momentToFormat.month()]; - } else { - return monthsNominative[momentToFormat.month()]; - } - }, - monthsShort : 'sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru'.split('_'), - weekdays : 'niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota'.split('_'), - weekdaysShort : 'nie_pon_wt_śr_czw_pt_sb'.split('_'), - weekdaysMin : 'Nd_Pn_Wt_Śr_Cz_Pt_So'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Dziś o] LT', - nextDay: '[Jutro o] LT', - nextWeek: '[W] dddd [o] LT', - lastDay: '[Wczoraj o] LT', - lastWeek: function () { - switch (this.day()) { +var pl = moment.defineLocale('pl', { + months : function (momentToFormat, format) { + if (!momentToFormat) { + return monthsNominative; + } else if (format === '') { + // Hack: if format empty we know this is used to generate + // RegExp by moment. Give then back both valid forms of months + // in RegExp ready format. + return '(' + monthsSubjective[momentToFormat.month()] + '|' + monthsNominative[momentToFormat.month()] + ')'; + } else if (/D MMMM/.test(format)) { + return monthsSubjective[momentToFormat.month()]; + } else { + return monthsNominative[momentToFormat.month()]; + } + }, + monthsShort : 'sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru'.split('_'), + weekdays : 'niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota'.split('_'), + weekdaysShort : 'ndz_pon_wt_śr_czw_pt_sob'.split('_'), + weekdaysMin : 'Nd_Pn_Wt_Śr_Cz_Pt_So'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[Dziś o] LT', + nextDay: '[Jutro o] LT', + nextWeek: '[W] dddd [o] LT', + lastDay: '[Wczoraj o] LT', + lastWeek: function () { + switch (this.day()) { case 0: return '[W zeszłą niedzielę o] LT'; case 3: @@ -73,33 +75,33 @@ return '[W zeszłą sobotę o] LT'; default: return '[W zeszły] dddd [o] LT'; - } - }, - sameElse: 'L' + } }, - relativeTime : { - future : 'za %s', - past : '%s temu', - s : 'kilka sekund', - m : translate, - mm : translate, - h : translate, - hh : translate, - d : '1 dzień', - dd : '%d dni', - M : 'miesiąc', - MM : translate, - y : 'rok', - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); + sameElse: 'L' + }, + relativeTime : { + future : 'za %s', + past : '%s temu', + s : 'kilka sekund', + m : translate, + mm : translate, + h : translate, + hh : translate, + d : '1 dzień', + dd : '%d dni', + M : 'miesiąc', + MM : translate, + y : 'rok', + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return pl; +return pl; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/pt-br.js b/lib/javascripts/moment_locale/pt-br.js old mode 100755 new mode 100644 index 84e6cdd1c2..dbcd0fdc1a --- a/lib/javascripts/moment_locale/pt-br.js +++ b/lib/javascripts/moment_locale/pt-br.js @@ -1,61 +1,61 @@ //! moment.js locale configuration -//! locale : brazilian portuguese (pt-br) +//! locale : Portuguese (Brazil) [pt-br] //! author : Caio Ribeiro Pereira : https://github.com/caio-ribeiro-pereira ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var pt_br = moment.defineLocale('pt-br', { - months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'), - monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'), - weekdays : 'Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado'.split('_'), - weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'), - weekdaysMin : 'Dom_2ª_3ª_4ª_5ª_6ª_Sáb'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY [às] HH:mm', - LLLL : 'dddd, D [de] MMMM [de] YYYY [às] HH:mm' +var ptBr = moment.defineLocale('pt-br', { + months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'), + monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'), + weekdays : 'Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado'.split('_'), + weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'), + weekdaysMin : 'Do_2ª_3ª_4ª_5ª_6ª_Sá'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D [de] MMMM [de] YYYY', + LLL : 'D [de] MMMM [de] YYYY [às] HH:mm', + LLLL : 'dddd, D [de] MMMM [de] YYYY [às] HH:mm' + }, + calendar : { + sameDay: '[Hoje às] LT', + nextDay: '[Amanhã às] LT', + nextWeek: 'dddd [às] LT', + lastDay: '[Ontem às] LT', + lastWeek: function () { + return (this.day() === 0 || this.day() === 6) ? + '[Último] dddd [às] LT' : // Saturday + Sunday + '[Última] dddd [às] LT'; // Monday - Friday }, - calendar : { - sameDay: '[Hoje às] LT', - nextDay: '[Amanhã às] LT', - nextWeek: 'dddd [às] LT', - lastDay: '[Ontem às] LT', - lastWeek: function () { - return (this.day() === 0 || this.day() === 6) ? - '[Último] dddd [às] LT' : // Saturday + Sunday - '[Última] dddd [às] LT'; // Monday - Friday - }, - sameElse: 'L' - }, - relativeTime : { - future : 'em %s', - past : '%s atrás', - s : 'poucos segundos', - m : 'um minuto', - mm : '%d minutos', - h : 'uma hora', - hh : '%d horas', - d : 'um dia', - dd : '%d dias', - M : 'um mês', - MM : '%d meses', - y : 'um ano', - yy : '%d anos' - }, - ordinalParse: /\d{1,2}º/, - ordinal : '%dº' - }); + sameElse: 'L' + }, + relativeTime : { + future : 'em %s', + past : '%s atrás', + s : 'poucos segundos', + m : 'um minuto', + mm : '%d minutos', + h : 'uma hora', + hh : '%d horas', + d : 'um dia', + dd : '%d dias', + M : 'um mês', + MM : '%d meses', + y : 'um ano', + yy : '%d anos' + }, + dayOfMonthOrdinalParse: /\d{1,2}º/, + ordinal : '%dº' +}); - return pt_br; +return ptBr; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/pt.js b/lib/javascripts/moment_locale/pt.js old mode 100755 new mode 100644 index b7fcd5abe4..2d7175cda6 --- a/lib/javascripts/moment_locale/pt.js +++ b/lib/javascripts/moment_locale/pt.js @@ -1,65 +1,65 @@ //! moment.js locale configuration -//! locale : portuguese (pt) +//! locale : Portuguese [pt] //! author : Jefferson : https://github.com/jalex79 ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var pt = moment.defineLocale('pt', { - months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'), - monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'), - weekdays : 'Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado'.split('_'), - weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'), - weekdaysMin : 'Dom_2ª_3ª_4ª_5ª_6ª_Sáb'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY HH:mm', - LLLL : 'dddd, D [de] MMMM [de] YYYY HH:mm' +var pt = moment.defineLocale('pt', { + months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'), + monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'), + weekdays : 'Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado'.split('_'), + weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'), + weekdaysMin : 'Do_2ª_3ª_4ª_5ª_6ª_Sá'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D [de] MMMM [de] YYYY', + LLL : 'D [de] MMMM [de] YYYY HH:mm', + LLLL : 'dddd, D [de] MMMM [de] YYYY HH:mm' + }, + calendar : { + sameDay: '[Hoje às] LT', + nextDay: '[Amanhã às] LT', + nextWeek: 'dddd [às] LT', + lastDay: '[Ontem às] LT', + lastWeek: function () { + return (this.day() === 0 || this.day() === 6) ? + '[Último] dddd [às] LT' : // Saturday + Sunday + '[Última] dddd [às] LT'; // Monday - Friday }, - calendar : { - sameDay: '[Hoje às] LT', - nextDay: '[Amanhã às] LT', - nextWeek: 'dddd [às] LT', - lastDay: '[Ontem às] LT', - lastWeek: function () { - return (this.day() === 0 || this.day() === 6) ? - '[Último] dddd [às] LT' : // Saturday + Sunday - '[Última] dddd [às] LT'; // Monday - Friday - }, - sameElse: 'L' - }, - relativeTime : { - future : 'em %s', - past : 'há %s', - s : 'segundos', - m : 'um minuto', - mm : '%d minutos', - h : 'uma hora', - hh : '%d horas', - d : 'um dia', - dd : '%d dias', - M : 'um mês', - MM : '%d meses', - y : 'um ano', - yy : '%d anos' - }, - ordinalParse: /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); + sameElse: 'L' + }, + relativeTime : { + future : 'em %s', + past : 'há %s', + s : 'segundos', + m : 'um minuto', + mm : '%d minutos', + h : 'uma hora', + hh : '%d horas', + d : 'um dia', + dd : '%d dias', + M : 'um mês', + MM : '%d meses', + y : 'um ano', + yy : '%d anos' + }, + dayOfMonthOrdinalParse: /\d{1,2}º/, + ordinal : '%dº', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return pt; +return pt; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ro.js b/lib/javascripts/moment_locale/ro.js old mode 100755 new mode 100644 index 09ffc193a9..8a0cd7501a --- a/lib/javascripts/moment_locale/ro.js +++ b/lib/javascripts/moment_locale/ro.js @@ -1,75 +1,75 @@ //! moment.js locale configuration -//! locale : romanian (ro) +//! locale : Romanian [ro] //! author : Vlad Gurdiga : https://github.com/gurdiga //! author : Valentin Agachi : https://github.com/avaly ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function relativeTimeWithPlural(number, withoutSuffix, key) { - var format = { - 'mm': 'minute', - 'hh': 'ore', - 'dd': 'zile', - 'MM': 'luni', - 'yy': 'ani' - }, - separator = ' '; - if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) { - separator = ' de '; - } - return number + separator + format[key]; +function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': 'minute', + 'hh': 'ore', + 'dd': 'zile', + 'MM': 'luni', + 'yy': 'ani' + }, + separator = ' '; + if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) { + separator = ' de '; } + return number + separator + format[key]; +} - var ro = moment.defineLocale('ro', { - months : 'ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie'.split('_'), - monthsShort : 'ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays : 'duminică_luni_marți_miercuri_joi_vineri_sâmbătă'.split('_'), - weekdaysShort : 'Dum_Lun_Mar_Mie_Joi_Vin_Sâm'.split('_'), - weekdaysMin : 'Du_Lu_Ma_Mi_Jo_Vi_Sâ'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd, D MMMM YYYY H:mm' - }, - calendar : { - sameDay: '[azi la] LT', - nextDay: '[mâine la] LT', - nextWeek: 'dddd [la] LT', - lastDay: '[ieri la] LT', - lastWeek: '[fosta] dddd [la] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'peste %s', - past : '%s în urmă', - s : 'câteva secunde', - m : 'un minut', - mm : relativeTimeWithPlural, - h : 'o oră', - hh : relativeTimeWithPlural, - d : 'o zi', - dd : relativeTimeWithPlural, - M : 'o lună', - MM : relativeTimeWithPlural, - y : 'un an', - yy : relativeTimeWithPlural - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); +var ro = moment.defineLocale('ro', { + months : 'ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie'.split('_'), + monthsShort : 'ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.'.split('_'), + monthsParseExact: true, + weekdays : 'duminică_luni_marți_miercuri_joi_vineri_sâmbătă'.split('_'), + weekdaysShort : 'Dum_Lun_Mar_Mie_Joi_Vin_Sâm'.split('_'), + weekdaysMin : 'Du_Lu_Ma_Mi_Jo_Vi_Sâ'.split('_'), + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY H:mm', + LLLL : 'dddd, D MMMM YYYY H:mm' + }, + calendar : { + sameDay: '[azi la] LT', + nextDay: '[mâine la] LT', + nextWeek: 'dddd [la] LT', + lastDay: '[ieri la] LT', + lastWeek: '[fosta] dddd [la] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'peste %s', + past : '%s în urmă', + s : 'câteva secunde', + m : 'un minut', + mm : relativeTimeWithPlural, + h : 'o oră', + hh : relativeTimeWithPlural, + d : 'o zi', + dd : relativeTimeWithPlural, + M : 'o lună', + MM : relativeTimeWithPlural, + y : 'un an', + yy : relativeTimeWithPlural + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return ro; +return ro; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ru.js b/lib/javascripts/moment_locale/ru.js old mode 100755 new mode 100644 index ec3df2e0a1..b45731e368 --- a/lib/javascripts/moment_locale/ru.js +++ b/lib/javascripts/moment_locale/ru.js @@ -1,5 +1,5 @@ //! moment.js locale configuration -//! locale : russian (ru) +//! locale : Russian [ru] //! author : Viktorminator : https://github.com/Viktorminator //! Author : Menelion Elensúle : https://github.com/Oire //! author : Коренберг Марк : https://github.com/socketpair @@ -7,74 +7,82 @@ ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function plural(word, num) { - var forms = word.split('_'); - return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +function plural(word, num) { + var forms = word.split('_'); + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +} +function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут', + 'hh': 'час_часа_часов', + 'dd': 'день_дня_дней', + 'MM': 'месяц_месяца_месяцев', + 'yy': 'год_года_лет' + }; + if (key === 'm') { + return withoutSuffix ? 'минута' : 'минуту'; } - function relativeTimeWithPlural(number, withoutSuffix, key) { - var format = { - 'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут', - 'hh': 'час_часа_часов', - 'dd': 'день_дня_дней', - 'MM': 'месяц_месяца_месяцев', - 'yy': 'год_года_лет' - }; - if (key === 'm') { - return withoutSuffix ? 'минута' : 'минуту'; - } - else { - return number + ' ' + plural(format[key], +number); - } + else { + return number + ' ' + plural(format[key], +number); } - var monthsParse = [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[йя]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i]; +} +var monthsParse = [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[йя]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i]; - // http://new.gramota.ru/spravka/rules/139-prop : § 103 - // Сокращения месяцев: http://new.gramota.ru/spravka/buro/search-answer?s=242637 - // CLDR data: http://www.unicode.org/cldr/charts/28/summary/ru.html#1753 - var ru = moment.defineLocale('ru', { - months : { - format: 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_'), - standalone: 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_') - }, - monthsShort : { - // по CLDR именно "июл." и "июн.", но какой смысл менять букву на точку ? - format: 'янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.'.split('_'), - standalone: 'янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.'.split('_') - }, - weekdays : { - standalone: 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'), - format: 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_'), - isFormat: /\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/ - }, - weekdaysShort : 'вс_пн_вт_ср_чт_пт_сб'.split('_'), - weekdaysMin : 'вс_пн_вт_ср_чт_пт_сб'.split('_'), - monthsParse : monthsParse, - longMonthsParse : monthsParse, - shortMonthsParse : monthsParse, - monthsRegex: /^(сентябр[яь]|октябр[яь]|декабр[яь]|феврал[яь]|январ[яь]|апрел[яь]|августа?|ноябр[яь]|сент\.|февр\.|нояб\.|июнь|янв.|июль|дек.|авг.|апр.|марта|мар[.т]|окт.|июн[яь]|июл[яь]|ма[яй])/i, - monthsShortRegex: /^(сентябр[яь]|октябр[яь]|декабр[яь]|феврал[яь]|январ[яь]|апрел[яь]|августа?|ноябр[яь]|сент\.|февр\.|нояб\.|июнь|янв.|июль|дек.|авг.|апр.|марта|мар[.т]|окт.|июн[яь]|июл[яь]|ма[яй])/i, - monthsStrictRegex: /^(сентябр[яь]|октябр[яь]|декабр[яь]|феврал[яь]|январ[яь]|апрел[яь]|августа?|ноябр[яь]|марта?|июн[яь]|июл[яь]|ма[яй])/i, - monthsShortStrictRegex: /^(нояб\.|февр\.|сент\.|июль|янв\.|июн[яь]|мар[.т]|авг\.|апр\.|окт\.|дек\.|ма[яй])/i, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY г.', - LLL : 'D MMMM YYYY г., HH:mm', - LLLL : 'dddd, D MMMM YYYY г., HH:mm' - }, - calendar : { - sameDay: '[Сегодня в] LT', - nextDay: '[Завтра в] LT', - lastDay: '[Вчера в] LT', - nextWeek: function (now) { - if (now.week() !== this.week()) { - switch (this.day()) { +// http://new.gramota.ru/spravka/rules/139-prop : § 103 +// Сокращения месяцев: http://new.gramota.ru/spravka/buro/search-answer?s=242637 +// CLDR data: http://www.unicode.org/cldr/charts/28/summary/ru.html#1753 +var ru = moment.defineLocale('ru', { + months : { + format: 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_'), + standalone: 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_') + }, + monthsShort : { + // по CLDR именно "июл." и "июн.", но какой смысл менять букву на точку ? + format: 'янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.'.split('_'), + standalone: 'янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.'.split('_') + }, + weekdays : { + standalone: 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'), + format: 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_'), + isFormat: /\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/ + }, + weekdaysShort : 'вс_пн_вт_ср_чт_пт_сб'.split('_'), + weekdaysMin : 'вс_пн_вт_ср_чт_пт_сб'.split('_'), + monthsParse : monthsParse, + longMonthsParse : monthsParse, + shortMonthsParse : monthsParse, + + // полные названия с падежами, по три буквы, для некоторых, по 4 буквы, сокращения с точкой и без точки + monthsRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i, + + // копия предыдущего + monthsShortRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i, + + // полные названия с падежами + monthsStrictRegex: /^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i, + + // Выражение, которое соотвествует только сокращённым формам + monthsShortStrictRegex: /^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY г.', + LLL : 'D MMMM YYYY г., HH:mm', + LLLL : 'dddd, D MMMM YYYY г., HH:mm' + }, + calendar : { + sameDay: '[Сегодня в] LT', + nextDay: '[Завтра в] LT', + lastDay: '[Вчера в] LT', + nextWeek: function (now) { + if (now.week() !== this.week()) { + switch (this.day()) { case 0: return '[В следующее] dddd [в] LT'; case 1: @@ -85,18 +93,18 @@ case 5: case 6: return '[В следующую] dddd [в] LT'; - } - } else { - if (this.day() === 2) { - return '[Во] dddd [в] LT'; - } else { - return '[В] dddd [в] LT'; - } } - }, - lastWeek: function (now) { - if (now.week() !== this.week()) { - switch (this.day()) { + } else { + if (this.day() === 2) { + return '[Во] dddd [в] LT'; + } else { + return '[В] dddd [в] LT'; + } + } + }, + lastWeek: function (now) { + if (now.week() !== this.week()) { + switch (this.day()) { case 0: return '[В прошлое] dddd [в] LT'; case 1: @@ -107,50 +115,50 @@ case 5: case 6: return '[В прошлую] dddd [в] LT'; - } - } else { - if (this.day() === 2) { - return '[Во] dddd [в] LT'; - } else { - return '[В] dddd [в] LT'; - } } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'через %s', - past : '%s назад', - s : 'несколько секунд', - m : relativeTimeWithPlural, - mm : relativeTimeWithPlural, - h : 'час', - hh : relativeTimeWithPlural, - d : 'день', - dd : relativeTimeWithPlural, - M : 'месяц', - MM : relativeTimeWithPlural, - y : 'год', - yy : relativeTimeWithPlural - }, - meridiemParse: /ночи|утра|дня|вечера/i, - isPM : function (input) { - return /^(дня|вечера)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ночи'; - } else if (hour < 12) { - return 'утра'; - } else if (hour < 17) { - return 'дня'; } else { - return 'вечера'; + if (this.day() === 2) { + return '[Во] dddd [в] LT'; + } else { + return '[В] dddd [в] LT'; + } } }, - ordinalParse: /\d{1,2}-(й|го|я)/, - ordinal: function (number, period) { - switch (period) { + sameElse: 'L' + }, + relativeTime : { + future : 'через %s', + past : '%s назад', + s : 'несколько секунд', + m : relativeTimeWithPlural, + mm : relativeTimeWithPlural, + h : 'час', + hh : relativeTimeWithPlural, + d : 'день', + dd : relativeTimeWithPlural, + M : 'месяц', + MM : relativeTimeWithPlural, + y : 'год', + yy : relativeTimeWithPlural + }, + meridiemParse: /ночи|утра|дня|вечера/i, + isPM : function (input) { + return /^(дня|вечера)$/.test(input); + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'ночи'; + } else if (hour < 12) { + return 'утра'; + } else if (hour < 17) { + return 'дня'; + } else { + return 'вечера'; + } + }, + dayOfMonthOrdinalParse: /\d{1,2}-(й|го|я)/, + ordinal: function (number, period) { + switch (period) { case 'M': case 'd': case 'DDD': @@ -162,14 +170,14 @@ return number + '-я'; default: return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return ru; +return ru; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/sd.js b/lib/javascripts/moment_locale/sd.js new file mode 100644 index 0000000000..0fcf7e10cc --- /dev/null +++ b/lib/javascripts/moment_locale/sd.js @@ -0,0 +1,98 @@ +//! moment.js locale configuration +//! locale : Sindhi [sd] +//! author : Narain Sagar : https://github.com/narainsagar + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var months = [ + 'جنوري', + 'فيبروري', + 'مارچ', + 'اپريل', + 'مئي', + 'جون', + 'جولاءِ', + 'آگسٽ', + 'سيپٽمبر', + 'آڪٽوبر', + 'نومبر', + 'ڊسمبر' +]; +var days = [ + 'آچر', + 'سومر', + 'اڱارو', + 'اربع', + 'خميس', + 'جمع', + 'ڇنڇر' +]; + +var sd = moment.defineLocale('sd', { + months : months, + monthsShort : months, + weekdays : days, + weekdaysShort : days, + weekdaysMin : days, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd، D MMMM YYYY HH:mm' + }, + meridiemParse: /صبح|شام/, + isPM : function (input) { + return 'شام' === input; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'صبح'; + } + return 'شام'; + }, + calendar : { + sameDay : '[اڄ] LT', + nextDay : '[سڀاڻي] LT', + nextWeek : 'dddd [اڳين هفتي تي] LT', + lastDay : '[ڪالهه] LT', + lastWeek : '[گزريل هفتي] dddd [تي] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s پوء', + past : '%s اڳ', + s : 'چند سيڪنڊ', + m : 'هڪ منٽ', + mm : '%d منٽ', + h : 'هڪ ڪلاڪ', + hh : '%d ڪلاڪ', + d : 'هڪ ڏينهن', + dd : '%d ڏينهن', + M : 'هڪ مهينو', + MM : '%d مهينا', + y : 'هڪ سال', + yy : '%d سال' + }, + preparse: function (string) { + return string.replace(/،/g, ','); + }, + postformat: function (string) { + return string.replace(/,/g, '،'); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return sd; + +}))); diff --git a/lib/javascripts/moment_locale/se.js b/lib/javascripts/moment_locale/se.js old mode 100755 new mode 100644 index 06be0d6ba1..d44d0df24c --- a/lib/javascripts/moment_locale/se.js +++ b/lib/javascripts/moment_locale/se.js @@ -1,61 +1,61 @@ //! moment.js locale configuration -//! locale : Northern Sami (se) +//! locale : Northern Sami [se] //! authors : Bård Rolstad Henriksen : https://github.com/karamell ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var se = moment.defineLocale('se', { - months : 'ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu'.split('_'), - monthsShort : 'ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov'.split('_'), - weekdays : 'sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat'.split('_'), - weekdaysShort : 'sotn_vuos_maŋ_gask_duor_bear_láv'.split('_'), - weekdaysMin : 's_v_m_g_d_b_L'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'MMMM D. [b.] YYYY', - LLL : 'MMMM D. [b.] YYYY [ti.] HH:mm', - LLLL : 'dddd, MMMM D. [b.] YYYY [ti.] HH:mm' - }, - calendar : { - sameDay: '[otne ti] LT', - nextDay: '[ihttin ti] LT', - nextWeek: 'dddd [ti] LT', - lastDay: '[ikte ti] LT', - lastWeek: '[ovddit] dddd [ti] LT', - sameElse: 'L' - }, - relativeTime : { - future : '%s geažes', - past : 'maŋit %s', - s : 'moadde sekunddat', - m : 'okta minuhta', - mm : '%d minuhtat', - h : 'okta diimmu', - hh : '%d diimmut', - d : 'okta beaivi', - dd : '%d beaivvit', - M : 'okta mánnu', - MM : '%d mánut', - y : 'okta jahki', - yy : '%d jagit' - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var se = moment.defineLocale('se', { + months : 'ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu'.split('_'), + monthsShort : 'ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov'.split('_'), + weekdays : 'sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat'.split('_'), + weekdaysShort : 'sotn_vuos_maŋ_gask_duor_bear_láv'.split('_'), + weekdaysMin : 's_v_m_g_d_b_L'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'MMMM D. [b.] YYYY', + LLL : 'MMMM D. [b.] YYYY [ti.] HH:mm', + LLLL : 'dddd, MMMM D. [b.] YYYY [ti.] HH:mm' + }, + calendar : { + sameDay: '[otne ti] LT', + nextDay: '[ihttin ti] LT', + nextWeek: 'dddd [ti] LT', + lastDay: '[ikte ti] LT', + lastWeek: '[ovddit] dddd [ti] LT', + sameElse: 'L' + }, + relativeTime : { + future : '%s geažes', + past : 'maŋit %s', + s : 'moadde sekunddat', + m : 'okta minuhta', + mm : '%d minuhtat', + h : 'okta diimmu', + hh : '%d diimmut', + d : 'okta beaivi', + dd : '%d beaivvit', + M : 'okta mánnu', + MM : '%d mánut', + y : 'okta jahki', + yy : '%d jagit' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return se; +return se; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/si.js b/lib/javascripts/moment_locale/si.js old mode 100755 new mode 100644 index 8a7716443e..f5c124ee45 --- a/lib/javascripts/moment_locale/si.js +++ b/lib/javascripts/moment_locale/si.js @@ -1,71 +1,71 @@ //! moment.js locale configuration -//! locale : Sinhalese (si) +//! locale : Sinhalese [si] //! author : Sampath Sitinamaluwa : https://github.com/sampathsris ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - /*jshint -W100*/ - var si = moment.defineLocale('si', { - months : 'ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්'.split('_'), - monthsShort : 'ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ'.split('_'), - weekdays : 'ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා'.split('_'), - weekdaysShort : 'ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන'.split('_'), - weekdaysMin : 'ඉ_ස_අ_බ_බ්‍ර_සි_සෙ'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'a h:mm', - LTS : 'a h:mm:ss', - L : 'YYYY/MM/DD', - LL : 'YYYY MMMM D', - LLL : 'YYYY MMMM D, a h:mm', - LLLL : 'YYYY MMMM D [වැනි] dddd, a h:mm:ss' - }, - calendar : { - sameDay : '[අද] LT[ට]', - nextDay : '[හෙට] LT[ට]', - nextWeek : 'dddd LT[ට]', - lastDay : '[ඊයේ] LT[ට]', - lastWeek : '[පසුගිය] dddd LT[ට]', - sameElse : 'L' - }, - relativeTime : { - future : '%sකින්', - past : '%sකට පෙර', - s : 'තත්පර කිහිපය', - m : 'මිනිත්තුව', - mm : 'මිනිත්තු %d', - h : 'පැය', - hh : 'පැය %d', - d : 'දිනය', - dd : 'දින %d', - M : 'මාසය', - MM : 'මාස %d', - y : 'වසර', - yy : 'වසර %d' - }, - ordinalParse: /\d{1,2} වැනි/, - ordinal : function (number) { - return number + ' වැනි'; - }, - meridiemParse : /පෙර වරු|පස් වරු|පෙ.ව|ප.ව./, - isPM : function (input) { - return input === 'ප.ව.' || input === 'පස් වරු'; - }, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'ප.ව.' : 'පස් වරු'; - } else { - return isLower ? 'පෙ.ව.' : 'පෙර වරු'; - } +/*jshint -W100*/ +var si = moment.defineLocale('si', { + months : 'ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්'.split('_'), + monthsShort : 'ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ'.split('_'), + weekdays : 'ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා'.split('_'), + weekdaysShort : 'ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන'.split('_'), + weekdaysMin : 'ඉ_ස_අ_බ_බ්‍ර_සි_සෙ'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'a h:mm', + LTS : 'a h:mm:ss', + L : 'YYYY/MM/DD', + LL : 'YYYY MMMM D', + LLL : 'YYYY MMMM D, a h:mm', + LLLL : 'YYYY MMMM D [වැනි] dddd, a h:mm:ss' + }, + calendar : { + sameDay : '[අද] LT[ට]', + nextDay : '[හෙට] LT[ට]', + nextWeek : 'dddd LT[ට]', + lastDay : '[ඊයේ] LT[ට]', + lastWeek : '[පසුගිය] dddd LT[ට]', + sameElse : 'L' + }, + relativeTime : { + future : '%sකින්', + past : '%sකට පෙර', + s : 'තත්පර කිහිපය', + m : 'මිනිත්තුව', + mm : 'මිනිත්තු %d', + h : 'පැය', + hh : 'පැය %d', + d : 'දිනය', + dd : 'දින %d', + M : 'මාසය', + MM : 'මාස %d', + y : 'වසර', + yy : 'වසර %d' + }, + dayOfMonthOrdinalParse: /\d{1,2} වැනි/, + ordinal : function (number) { + return number + ' වැනි'; + }, + meridiemParse : /පෙර වරු|පස් වරු|පෙ.ව|ප.ව./, + isPM : function (input) { + return input === 'ප.ව.' || input === 'පස් වරු'; + }, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'ප.ව.' : 'පස් වරු'; + } else { + return isLower ? 'පෙ.ව.' : 'පෙර වරු'; } - }); + } +}); - return si; +return si; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/sk.js b/lib/javascripts/moment_locale/sk.js old mode 100755 new mode 100644 index 52838a3a62..aa61da621e --- a/lib/javascripts/moment_locale/sk.js +++ b/lib/javascripts/moment_locale/sk.js @@ -1,24 +1,24 @@ //! moment.js locale configuration -//! locale : slovak (sk) +//! locale : Slovak [sk] //! author : Martin Minka : https://github.com/k2s //! based on work of petrbela : https://github.com/petrbela ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var months = 'január_február_marec_apríl_máj_jún_júl_august_september_október_november_december'.split('_'), - monthsShort = 'jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec'.split('_'); - function plural(n) { - return (n > 1) && (n < 5); - } - function translate(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { +var months = 'január_február_marec_apríl_máj_jún_júl_august_september_október_november_december'.split('_'); +var monthsShort = 'jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec'.split('_'); +function plural(n) { + return (n > 1) && (n < 5); +} +function translate(number, withoutSuffix, key, isFuture) { + var result = number + ' '; + switch (key) { case 's': // a few seconds / in a few seconds / a few seconds ago return (withoutSuffix || isFuture) ? 'pár sekúnd' : 'pár sekundami'; case 'm': // a minute / in a minute / a minute ago @@ -66,28 +66,28 @@ return result + 'rokmi'; } break; - } } +} - var sk = moment.defineLocale('sk', { - months : months, - monthsShort : monthsShort, - weekdays : 'nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota'.split('_'), - weekdaysShort : 'ne_po_ut_st_št_pi_so'.split('_'), - weekdaysMin : 'ne_po_ut_st_št_pi_so'.split('_'), - longDateFormat : { - LT: 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd D. MMMM YYYY H:mm' - }, - calendar : { - sameDay: '[dnes o] LT', - nextDay: '[zajtra o] LT', - nextWeek: function () { - switch (this.day()) { +var sk = moment.defineLocale('sk', { + months : months, + monthsShort : monthsShort, + weekdays : 'nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota'.split('_'), + weekdaysShort : 'ne_po_ut_st_št_pi_so'.split('_'), + weekdaysMin : 'ne_po_ut_st_št_pi_so'.split('_'), + longDateFormat : { + LT: 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY H:mm', + LLLL : 'dddd D. MMMM YYYY H:mm' + }, + calendar : { + sameDay: '[dnes o] LT', + nextDay: '[zajtra o] LT', + nextWeek: function () { + switch (this.day()) { case 0: return '[v nedeľu o] LT'; case 1: @@ -101,11 +101,11 @@ return '[v piatok o] LT'; case 6: return '[v sobotu o] LT'; - } - }, - lastDay: '[včera o] LT', - lastWeek: function () { - switch (this.day()) { + } + }, + lastDay: '[včera o] LT', + lastWeek: function () { + switch (this.day()) { case 0: return '[minulú nedeľu o] LT'; case 1: @@ -118,33 +118,33 @@ return '[minulý] dddd [o] LT'; case 6: return '[minulú sobotu o] LT'; - } - }, - sameElse: 'L' + } }, - relativeTime : { - future : 'za %s', - past : 'pred %s', - s : translate, - m : translate, - mm : translate, - h : translate, - hh : translate, - d : translate, - dd : translate, - M : translate, - MM : translate, - y : translate, - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); + sameElse: 'L' + }, + relativeTime : { + future : 'za %s', + past : 'pred %s', + s : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return sk; +return sk; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/sl.js b/lib/javascripts/moment_locale/sl.js old mode 100755 new mode 100644 index a6bb706cdc..e74b3a11c1 --- a/lib/javascripts/moment_locale/sl.js +++ b/lib/javascripts/moment_locale/sl.js @@ -1,18 +1,18 @@ //! moment.js locale configuration -//! locale : slovenian (sl) +//! locale : Slovenian [sl] //! author : Robert Sedovšek : https://github.com/sedovsek ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function processRelativeTime(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var result = number + ' '; + switch (key) { case 's': return withoutSuffix || isFuture ? 'nekaj sekund' : 'nekaj sekundami'; case 'm': @@ -78,31 +78,31 @@ result += withoutSuffix || isFuture ? 'let' : 'leti'; } return result; - } } +} - var sl = moment.defineLocale('sl', { - months : 'januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december'.split('_'), - monthsShort : 'jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays : 'nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota'.split('_'), - weekdaysShort : 'ned._pon._tor._sre._čet._pet._sob.'.split('_'), - weekdaysMin : 'ne_po_to_sr_če_pe_so'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD. MM. YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[danes ob] LT', - nextDay : '[jutri ob] LT', +var sl = moment.defineLocale('sl', { + months : 'januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december'.split('_'), + monthsShort : 'jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.'.split('_'), + monthsParseExact: true, + weekdays : 'nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota'.split('_'), + weekdaysShort : 'ned._pon._tor._sre._čet._pet._sob.'.split('_'), + weekdaysMin : 'ne_po_to_sr_če_pe_so'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY H:mm', + LLLL : 'dddd, D. MMMM YYYY H:mm' + }, + calendar : { + sameDay : '[danes ob] LT', + nextDay : '[jutri ob] LT', - nextWeek : function () { - switch (this.day()) { + nextWeek : function () { + switch (this.day()) { case 0: return '[v] [nedeljo] [ob] LT'; case 3: @@ -114,11 +114,11 @@ case 4: case 5: return '[v] dddd [ob] LT'; - } - }, - lastDay : '[včeraj ob] LT', - lastWeek : function () { - switch (this.day()) { + } + }, + lastDay : '[včeraj ob] LT', + lastWeek : function () { + switch (this.day()) { case 0: return '[prejšnjo] [nedeljo] [ob] LT'; case 3: @@ -130,33 +130,33 @@ case 4: case 5: return '[prejšnji] dddd [ob] LT'; - } - }, - sameElse : 'L' + } }, - relativeTime : { - future : 'čez %s', - past : 'pred %s', - s : processRelativeTime, - m : processRelativeTime, - mm : processRelativeTime, - h : processRelativeTime, - hh : processRelativeTime, - d : processRelativeTime, - dd : processRelativeTime, - M : processRelativeTime, - MM : processRelativeTime, - y : processRelativeTime, - yy : processRelativeTime - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + sameElse : 'L' + }, + relativeTime : { + future : 'čez %s', + past : 'pred %s', + s : processRelativeTime, + m : processRelativeTime, + mm : processRelativeTime, + h : processRelativeTime, + hh : processRelativeTime, + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return sl; +return sl; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/sq.js b/lib/javascripts/moment_locale/sq.js old mode 100755 new mode 100644 index 68dbc1461a..b92dc1726c --- a/lib/javascripts/moment_locale/sq.js +++ b/lib/javascripts/moment_locale/sq.js @@ -1,70 +1,70 @@ //! moment.js locale configuration -//! locale : Albanian (sq) +//! locale : Albanian [sq] //! author : Flakërim Ismani : https://github.com/flakerimi -//! author: Menelion Elensúle: https://github.com/Oire (tests) -//! author : Oerd Cukalla : https://github.com/oerd (fixes) +//! author : Menelion Elensúle : https://github.com/Oire +//! author : Oerd Cukalla : https://github.com/oerd ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var sq = moment.defineLocale('sq', { - months : 'Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor'.split('_'), - monthsShort : 'Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj'.split('_'), - weekdays : 'E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë'.split('_'), - weekdaysShort : 'Die_Hën_Mar_Mër_Enj_Pre_Sht'.split('_'), - weekdaysMin : 'D_H_Ma_Më_E_P_Sh'.split('_'), - weekdaysParseExact : true, - meridiemParse: /PD|MD/, - isPM: function (input) { - return input.charAt(0) === 'M'; - }, - meridiem : function (hours, minutes, isLower) { - return hours < 12 ? 'PD' : 'MD'; - }, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Sot në] LT', - nextDay : '[Nesër në] LT', - nextWeek : 'dddd [në] LT', - lastDay : '[Dje në] LT', - lastWeek : 'dddd [e kaluar në] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'në %s', - past : '%s më parë', - s : 'disa sekonda', - m : 'një minutë', - mm : '%d minuta', - h : 'një orë', - hh : '%d orë', - d : 'një ditë', - dd : '%d ditë', - M : 'një muaj', - MM : '%d muaj', - y : 'një vit', - yy : '%d vite' - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var sq = moment.defineLocale('sq', { + months : 'Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor'.split('_'), + monthsShort : 'Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj'.split('_'), + weekdays : 'E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë'.split('_'), + weekdaysShort : 'Die_Hën_Mar_Mër_Enj_Pre_Sht'.split('_'), + weekdaysMin : 'D_H_Ma_Më_E_P_Sh'.split('_'), + weekdaysParseExact : true, + meridiemParse: /PD|MD/, + isPM: function (input) { + return input.charAt(0) === 'M'; + }, + meridiem : function (hours, minutes, isLower) { + return hours < 12 ? 'PD' : 'MD'; + }, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[Sot në] LT', + nextDay : '[Nesër në] LT', + nextWeek : 'dddd [në] LT', + lastDay : '[Dje në] LT', + lastWeek : 'dddd [e kaluar në] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'në %s', + past : '%s më parë', + s : 'disa sekonda', + m : 'një minutë', + mm : '%d minuta', + h : 'një orë', + hh : '%d orë', + d : 'një ditë', + dd : '%d ditë', + M : 'një muaj', + MM : '%d muaj', + y : 'një vit', + yy : '%d vite' + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return sq; +return sq; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/sr-cyrl.js b/lib/javascripts/moment_locale/sr-cyrl.js old mode 100755 new mode 100644 index 42f9ed4bdb..3007819bd2 --- a/lib/javascripts/moment_locale/sr-cyrl.js +++ b/lib/javascripts/moment_locale/sr-cyrl.js @@ -1,59 +1,59 @@ //! moment.js locale configuration -//! locale : Serbian-cyrillic (sr-cyrl) +//! locale : Serbian Cyrillic [sr-cyrl] //! author : Milan Janačković : https://github.com/milan-j ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var translator = { - words: { //Different grammatical cases - m: ['један минут', 'једне минуте'], - mm: ['минут', 'минуте', 'минута'], - h: ['један сат', 'једног сата'], - hh: ['сат', 'сата', 'сати'], - dd: ['дан', 'дана', 'дана'], - MM: ['месец', 'месеца', 'месеци'], - yy: ['година', 'године', 'година'] - }, - correctGrammaticalCase: function (number, wordKey) { - return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); - }, - translate: function (number, withoutSuffix, key) { - var wordKey = translator.words[key]; - if (key.length === 1) { - return withoutSuffix ? wordKey[0] : wordKey[1]; - } else { - return number + ' ' + translator.correctGrammaticalCase(number, wordKey); - } +var translator = { + words: { //Different grammatical cases + m: ['један минут', 'једне минуте'], + mm: ['минут', 'минуте', 'минута'], + h: ['један сат', 'једног сата'], + hh: ['сат', 'сата', 'сати'], + dd: ['дан', 'дана', 'дана'], + MM: ['месец', 'месеца', 'месеци'], + yy: ['година', 'године', 'година'] + }, + correctGrammaticalCase: function (number, wordKey) { + return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); + }, + translate: function (number, withoutSuffix, key) { + var wordKey = translator.words[key]; + if (key.length === 1) { + return withoutSuffix ? wordKey[0] : wordKey[1]; + } else { + return number + ' ' + translator.correctGrammaticalCase(number, wordKey); } - }; + } +}; - var sr_cyrl = moment.defineLocale('sr-cyrl', { - months: 'јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар'.split('_'), - monthsShort: 'јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.'.split('_'), - monthsParseExact: true, - weekdays: 'недеља_понедељак_уторак_среда_четвртак_петак_субота'.split('_'), - weekdaysShort: 'нед._пон._уто._сре._чет._пет._суб.'.split('_'), - weekdaysMin: 'не_по_ут_ср_че_пе_су'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm', - LTS : 'H:mm:ss', - L: 'DD. MM. YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' - }, - calendar: { - sameDay: '[данас у] LT', - nextDay: '[сутра у] LT', - nextWeek: function () { - switch (this.day()) { +var srCyrl = moment.defineLocale('sr-cyrl', { + months: 'јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар'.split('_'), + monthsShort: 'јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.'.split('_'), + monthsParseExact: true, + weekdays: 'недеља_понедељак_уторак_среда_четвртак_петак_субота'.split('_'), + weekdaysShort: 'нед._пон._уто._сре._чет._пет._суб.'.split('_'), + weekdaysMin: 'не_по_ут_ср_че_пе_су'.split('_'), + weekdaysParseExact : true, + longDateFormat: { + LT: 'H:mm', + LTS : 'H:mm:ss', + L: 'DD.MM.YYYY', + LL: 'D. MMMM YYYY', + LLL: 'D. MMMM YYYY H:mm', + LLLL: 'dddd, D. MMMM YYYY H:mm' + }, + calendar: { + sameDay: '[данас у] LT', + nextDay: '[сутра у] LT', + nextWeek: function () { + switch (this.day()) { case 0: return '[у] [недељу] [у] LT'; case 3: @@ -65,46 +65,46 @@ case 4: case 5: return '[у] dddd [у] LT'; - } - }, - lastDay : '[јуче у] LT', - lastWeek : function () { - var lastWeekDays = [ - '[прошле] [недеље] [у] LT', - '[прошлог] [понедељка] [у] LT', - '[прошлог] [уторка] [у] LT', - '[прошле] [среде] [у] LT', - '[прошлог] [четвртка] [у] LT', - '[прошлог] [петка] [у] LT', - '[прошле] [суботе] [у] LT' - ]; - return lastWeekDays[this.day()]; - }, - sameElse : 'L' + } }, - relativeTime : { - future : 'за %s', - past : 'пре %s', - s : 'неколико секунди', - m : translator.translate, - mm : translator.translate, - h : translator.translate, - hh : translator.translate, - d : 'дан', - dd : translator.translate, - M : 'месец', - MM : translator.translate, - y : 'годину', - yy : translator.translate + lastDay : '[јуче у] LT', + lastWeek : function () { + var lastWeekDays = [ + '[прошле] [недеље] [у] LT', + '[прошлог] [понедељка] [у] LT', + '[прошлог] [уторка] [у] LT', + '[прошле] [среде] [у] LT', + '[прошлог] [четвртка] [у] LT', + '[прошлог] [петка] [у] LT', + '[прошле] [суботе] [у] LT' + ]; + return lastWeekDays[this.day()]; }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + sameElse : 'L' + }, + relativeTime : { + future : 'за %s', + past : 'пре %s', + s : 'неколико секунди', + m : translator.translate, + mm : translator.translate, + h : translator.translate, + hh : translator.translate, + d : 'дан', + dd : translator.translate, + M : 'месец', + MM : translator.translate, + y : 'годину', + yy : translator.translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return sr_cyrl; +return srCyrl; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/sr.js b/lib/javascripts/moment_locale/sr.js old mode 100755 new mode 100644 index d334e78d5c..90778a9bfa --- a/lib/javascripts/moment_locale/sr.js +++ b/lib/javascripts/moment_locale/sr.js @@ -1,59 +1,59 @@ //! moment.js locale configuration -//! locale : Serbian-latin (sr) +//! locale : Serbian [sr] //! author : Milan Janačković : https://github.com/milan-j ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var translator = { - words: { //Different grammatical cases - m: ['jedan minut', 'jedne minute'], - mm: ['minut', 'minute', 'minuta'], - h: ['jedan sat', 'jednog sata'], - hh: ['sat', 'sata', 'sati'], - dd: ['dan', 'dana', 'dana'], - MM: ['mesec', 'meseca', 'meseci'], - yy: ['godina', 'godine', 'godina'] - }, - correctGrammaticalCase: function (number, wordKey) { - return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); - }, - translate: function (number, withoutSuffix, key) { - var wordKey = translator.words[key]; - if (key.length === 1) { - return withoutSuffix ? wordKey[0] : wordKey[1]; - } else { - return number + ' ' + translator.correctGrammaticalCase(number, wordKey); - } +var translator = { + words: { //Different grammatical cases + m: ['jedan minut', 'jedne minute'], + mm: ['minut', 'minute', 'minuta'], + h: ['jedan sat', 'jednog sata'], + hh: ['sat', 'sata', 'sati'], + dd: ['dan', 'dana', 'dana'], + MM: ['mesec', 'meseca', 'meseci'], + yy: ['godina', 'godine', 'godina'] + }, + correctGrammaticalCase: function (number, wordKey) { + return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); + }, + translate: function (number, withoutSuffix, key) { + var wordKey = translator.words[key]; + if (key.length === 1) { + return withoutSuffix ? wordKey[0] : wordKey[1]; + } else { + return number + ' ' + translator.correctGrammaticalCase(number, wordKey); } - }; + } +}; - var sr = moment.defineLocale('sr', { - months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'), - monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays: 'nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota'.split('_'), - weekdaysShort: 'ned._pon._uto._sre._čet._pet._sub.'.split('_'), - weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm', - LTS : 'H:mm:ss', - L: 'DD. MM. YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' - }, - calendar: { - sameDay: '[danas u] LT', - nextDay: '[sutra u] LT', - nextWeek: function () { - switch (this.day()) { +var sr = moment.defineLocale('sr', { + months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'), + monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'), + monthsParseExact: true, + weekdays: 'nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota'.split('_'), + weekdaysShort: 'ned._pon._uto._sre._čet._pet._sub.'.split('_'), + weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'), + weekdaysParseExact : true, + longDateFormat: { + LT: 'H:mm', + LTS : 'H:mm:ss', + L: 'DD.MM.YYYY', + LL: 'D. MMMM YYYY', + LLL: 'D. MMMM YYYY H:mm', + LLLL: 'dddd, D. MMMM YYYY H:mm' + }, + calendar: { + sameDay: '[danas u] LT', + nextDay: '[sutra u] LT', + nextWeek: function () { + switch (this.day()) { case 0: return '[u] [nedelju] [u] LT'; case 3: @@ -65,46 +65,46 @@ case 4: case 5: return '[u] dddd [u] LT'; - } - }, - lastDay : '[juče u] LT', - lastWeek : function () { - var lastWeekDays = [ - '[prošle] [nedelje] [u] LT', - '[prošlog] [ponedeljka] [u] LT', - '[prošlog] [utorka] [u] LT', - '[prošle] [srede] [u] LT', - '[prošlog] [četvrtka] [u] LT', - '[prošlog] [petka] [u] LT', - '[prošle] [subote] [u] LT' - ]; - return lastWeekDays[this.day()]; - }, - sameElse : 'L' + } }, - relativeTime : { - future : 'za %s', - past : 'pre %s', - s : 'nekoliko sekundi', - m : translator.translate, - mm : translator.translate, - h : translator.translate, - hh : translator.translate, - d : 'dan', - dd : translator.translate, - M : 'mesec', - MM : translator.translate, - y : 'godinu', - yy : translator.translate + lastDay : '[juče u] LT', + lastWeek : function () { + var lastWeekDays = [ + '[prošle] [nedelje] [u] LT', + '[prošlog] [ponedeljka] [u] LT', + '[prošlog] [utorka] [u] LT', + '[prošle] [srede] [u] LT', + '[prošlog] [četvrtka] [u] LT', + '[prošlog] [petka] [u] LT', + '[prošle] [subote] [u] LT' + ]; + return lastWeekDays[this.day()]; }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); + sameElse : 'L' + }, + relativeTime : { + future : 'za %s', + past : 'pre %s', + s : 'nekoliko sekundi', + m : translator.translate, + mm : translator.translate, + h : translator.translate, + hh : translator.translate, + d : 'dan', + dd : translator.translate, + M : 'mesec', + MM : translator.translate, + y : 'godinu', + yy : translator.translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return sr; +return sr; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ss.js b/lib/javascripts/moment_locale/ss.js new file mode 100644 index 0000000000..e89e2db293 --- /dev/null +++ b/lib/javascripts/moment_locale/ss.js @@ -0,0 +1,89 @@ +//! moment.js locale configuration +//! locale : siSwati [ss] +//! author : Nicolai Davies : https://github.com/nicolaidavies + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + + +var ss = moment.defineLocale('ss', { + months : "Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split('_'), + monthsShort : 'Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo'.split('_'), + weekdays : 'Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo'.split('_'), + weekdaysShort : 'Lis_Umb_Lsb_Les_Lsi_Lsh_Umg'.split('_'), + weekdaysMin : 'Li_Us_Lb_Lt_Ls_Lh_Ug'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY h:mm A', + LLLL : 'dddd, D MMMM YYYY h:mm A' + }, + calendar : { + sameDay : '[Namuhla nga] LT', + nextDay : '[Kusasa nga] LT', + nextWeek : 'dddd [nga] LT', + lastDay : '[Itolo nga] LT', + lastWeek : 'dddd [leliphelile] [nga] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'nga %s', + past : 'wenteka nga %s', + s : 'emizuzwana lomcane', + m : 'umzuzu', + mm : '%d emizuzu', + h : 'lihora', + hh : '%d emahora', + d : 'lilanga', + dd : '%d emalanga', + M : 'inyanga', + MM : '%d tinyanga', + y : 'umnyaka', + yy : '%d iminyaka' + }, + meridiemParse: /ekuseni|emini|entsambama|ebusuku/, + meridiem : function (hours, minutes, isLower) { + if (hours < 11) { + return 'ekuseni'; + } else if (hours < 15) { + return 'emini'; + } else if (hours < 19) { + return 'entsambama'; + } else { + return 'ebusuku'; + } + }, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === 'ekuseni') { + return hour; + } else if (meridiem === 'emini') { + return hour >= 11 ? hour : hour + 12; + } else if (meridiem === 'entsambama' || meridiem === 'ebusuku') { + if (hour === 0) { + return 0; + } + return hour + 12; + } + }, + dayOfMonthOrdinalParse: /\d{1,2}/, + ordinal : '%d', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return ss; + +}))); diff --git a/lib/javascripts/moment_locale/sv.js b/lib/javascripts/moment_locale/sv.js old mode 100755 new mode 100644 index a21237dc6f..1aa7660779 --- a/lib/javascripts/moment_locale/sv.js +++ b/lib/javascripts/moment_locale/sv.js @@ -1,69 +1,69 @@ //! moment.js locale configuration -//! locale : swedish (sv) +//! locale : Swedish [sv] //! author : Jens Alm : https://github.com/ulmus ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var sv = moment.defineLocale('sv', { - months : 'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'), - monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'), - weekdays : 'söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag'.split('_'), - weekdaysShort : 'sön_mån_tis_ons_tor_fre_lör'.split('_'), - weekdaysMin : 'sö_må_ti_on_to_fr_lö'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [kl.] HH:mm', - LLLL : 'dddd D MMMM YYYY [kl.] HH:mm', - lll : 'D MMM YYYY HH:mm', - llll : 'ddd D MMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Idag] LT', - nextDay: '[Imorgon] LT', - lastDay: '[Igår] LT', - nextWeek: '[På] dddd LT', - lastWeek: '[I] dddd[s] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'om %s', - past : 'för %s sedan', - s : 'några sekunder', - m : 'en minut', - mm : '%d minuter', - h : 'en timme', - hh : '%d timmar', - d : 'en dag', - dd : '%d dagar', - M : 'en månad', - MM : '%d månader', - y : 'ett år', - yy : '%d år' - }, - ordinalParse: /\d{1,2}(e|a)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'e' : - (b === 1) ? 'a' : - (b === 2) ? 'a' : - (b === 3) ? 'e' : 'e'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var sv = moment.defineLocale('sv', { + months : 'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'), + monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'), + weekdays : 'söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag'.split('_'), + weekdaysShort : 'sön_mån_tis_ons_tor_fre_lör'.split('_'), + weekdaysMin : 'sö_må_ti_on_to_fr_lö'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY-MM-DD', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY [kl.] HH:mm', + LLLL : 'dddd D MMMM YYYY [kl.] HH:mm', + lll : 'D MMM YYYY HH:mm', + llll : 'ddd D MMM YYYY HH:mm' + }, + calendar : { + sameDay: '[Idag] LT', + nextDay: '[Imorgon] LT', + lastDay: '[Igår] LT', + nextWeek: '[På] dddd LT', + lastWeek: '[I] dddd[s] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'om %s', + past : 'för %s sedan', + s : 'några sekunder', + m : 'en minut', + mm : '%d minuter', + h : 'en timme', + hh : '%d timmar', + d : 'en dag', + dd : '%d dagar', + M : 'en månad', + MM : '%d månader', + y : 'ett år', + yy : '%d år' + }, + dayOfMonthOrdinalParse: /\d{1,2}(e|a)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'e' : + (b === 1) ? 'a' : + (b === 2) ? 'a' : + (b === 3) ? 'e' : 'e'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return sv; +return sv; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/sw.js b/lib/javascripts/moment_locale/sw.js old mode 100755 new mode 100644 index 1460edd417..9ed314f563 --- a/lib/javascripts/moment_locale/sw.js +++ b/lib/javascripts/moment_locale/sw.js @@ -1,59 +1,59 @@ //! moment.js locale configuration -//! locale : swahili (sw) +//! locale : Swahili [sw] //! author : Fahad Kassim : https://github.com/fadsel ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var sw = moment.defineLocale('sw', { - months : 'Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba'.split('_'), - monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des'.split('_'), - weekdays : 'Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi'.split('_'), - weekdaysShort : 'Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos'.split('_'), - weekdaysMin : 'J2_J3_J4_J5_Al_Ij_J1'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[leo saa] LT', - nextDay : '[kesho saa] LT', - nextWeek : '[wiki ijayo] dddd [saat] LT', - lastDay : '[jana] LT', - lastWeek : '[wiki iliyopita] dddd [saat] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s baadaye', - past : 'tokea %s', - s : 'hivi punde', - m : 'dakika moja', - mm : 'dakika %d', - h : 'saa limoja', - hh : 'masaa %d', - d : 'siku moja', - dd : 'masiku %d', - M : 'mwezi mmoja', - MM : 'miezi %d', - y : 'mwaka mmoja', - yy : 'miaka %d' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); +var sw = moment.defineLocale('sw', { + months : 'Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba'.split('_'), + monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des'.split('_'), + weekdays : 'Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi'.split('_'), + weekdaysShort : 'Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos'.split('_'), + weekdaysMin : 'J2_J3_J4_J5_Al_Ij_J1'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[leo saa] LT', + nextDay : '[kesho saa] LT', + nextWeek : '[wiki ijayo] dddd [saat] LT', + lastDay : '[jana] LT', + lastWeek : '[wiki iliyopita] dddd [saat] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s baadaye', + past : 'tokea %s', + s : 'hivi punde', + m : 'dakika moja', + mm : 'dakika %d', + h : 'saa limoja', + hh : 'masaa %d', + d : 'siku moja', + dd : 'masiku %d', + M : 'mwezi mmoja', + MM : 'miezi %d', + y : 'mwaka mmoja', + yy : 'miaka %d' + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return sw; +return sw; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ta.js b/lib/javascripts/moment_locale/ta.js old mode 100755 new mode 100644 index 2766509554..d9d2555ae8 --- a/lib/javascripts/moment_locale/ta.js +++ b/lib/javascripts/moment_locale/ta.js @@ -1,129 +1,130 @@ //! moment.js locale configuration -//! locale : tamil (ta) +//! locale : Tamil [ta] //! author : Arjunkumar Krishnamoorthy : https://github.com/tk120404 ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var symbolMap = { - '1': '௧', - '2': '௨', - '3': '௩', - '4': '௪', - '5': '௫', - '6': '௬', - '7': '௭', - '8': '௮', - '9': '௯', - '0': '௦' - }, numberMap = { - '௧': '1', - '௨': '2', - '௩': '3', - '௪': '4', - '௫': '5', - '௬': '6', - '௭': '7', - '௮': '8', - '௯': '9', - '௦': '0' - }; +var symbolMap = { + '1': '௧', + '2': '௨', + '3': '௩', + '4': '௪', + '5': '௫', + '6': '௬', + '7': '௭', + '8': '௮', + '9': '௯', + '0': '௦' +}; +var numberMap = { + '௧': '1', + '௨': '2', + '௩': '3', + '௪': '4', + '௫': '5', + '௬': '6', + '௭': '7', + '௮': '8', + '௯': '9', + '௦': '0' +}; - var ta = moment.defineLocale('ta', { - months : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'), - monthsShort : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'), - weekdays : 'ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை'.split('_'), - weekdaysShort : 'ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி'.split('_'), - weekdaysMin : 'ஞா_தி_செ_பு_வி_வெ_ச'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, HH:mm', - LLLL : 'dddd, D MMMM YYYY, HH:mm' - }, - calendar : { - sameDay : '[இன்று] LT', - nextDay : '[நாளை] LT', - nextWeek : 'dddd, LT', - lastDay : '[நேற்று] LT', - lastWeek : '[கடந்த வாரம்] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s இல்', - past : '%s முன்', - s : 'ஒரு சில விநாடிகள்', - m : 'ஒரு நிமிடம்', - mm : '%d நிமிடங்கள்', - h : 'ஒரு மணி நேரம்', - hh : '%d மணி நேரம்', - d : 'ஒரு நாள்', - dd : '%d நாட்கள்', - M : 'ஒரு மாதம்', - MM : '%d மாதங்கள்', - y : 'ஒரு வருடம்', - yy : '%d ஆண்டுகள்' - }, - ordinalParse: /\d{1,2}வது/, - ordinal : function (number) { - return number + 'வது'; - }, - preparse: function (string) { - return string.replace(/[௧௨௩௪௫௬௭௮௯௦]/g, function (match) { - return numberMap[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }); - }, - // refer http://ta.wikipedia.org/s/1er1 - meridiemParse: /யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/, - meridiem : function (hour, minute, isLower) { - if (hour < 2) { - return ' யாமம்'; - } else if (hour < 6) { - return ' வைகறை'; // வைகறை - } else if (hour < 10) { - return ' காலை'; // காலை - } else if (hour < 14) { - return ' நண்பகல்'; // நண்பகல் - } else if (hour < 18) { - return ' எற்பாடு'; // எற்பாடு - } else if (hour < 22) { - return ' மாலை'; // மாலை - } else { - return ' யாமம்'; - } - }, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'யாமம்') { - return hour < 2 ? hour : hour + 12; - } else if (meridiem === 'வைகறை' || meridiem === 'காலை') { - return hour; - } else if (meridiem === 'நண்பகல்') { - return hour >= 10 ? hour : hour + 12; - } else { - return hour + 12; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. +var ta = moment.defineLocale('ta', { + months : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'), + monthsShort : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'), + weekdays : 'ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை'.split('_'), + weekdaysShort : 'ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி'.split('_'), + weekdaysMin : 'ஞா_தி_செ_பு_வி_வெ_ச'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, HH:mm', + LLLL : 'dddd, D MMMM YYYY, HH:mm' + }, + calendar : { + sameDay : '[இன்று] LT', + nextDay : '[நாளை] LT', + nextWeek : 'dddd, LT', + lastDay : '[நேற்று] LT', + lastWeek : '[கடந்த வாரம்] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s இல்', + past : '%s முன்', + s : 'ஒரு சில விநாடிகள்', + m : 'ஒரு நிமிடம்', + mm : '%d நிமிடங்கள்', + h : 'ஒரு மணி நேரம்', + hh : '%d மணி நேரம்', + d : 'ஒரு நாள்', + dd : '%d நாட்கள்', + M : 'ஒரு மாதம்', + MM : '%d மாதங்கள்', + y : 'ஒரு வருடம்', + yy : '%d ஆண்டுகள்' + }, + dayOfMonthOrdinalParse: /\d{1,2}வது/, + ordinal : function (number) { + return number + 'வது'; + }, + preparse: function (string) { + return string.replace(/[௧௨௩௪௫௬௭௮௯௦]/g, function (match) { + return numberMap[match]; + }); + }, + postformat: function (string) { + return string.replace(/\d/g, function (match) { + return symbolMap[match]; + }); + }, + // refer http://ta.wikipedia.org/s/1er1 + meridiemParse: /யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/, + meridiem : function (hour, minute, isLower) { + if (hour < 2) { + return ' யாமம்'; + } else if (hour < 6) { + return ' வைகறை'; // வைகறை + } else if (hour < 10) { + return ' காலை'; // காலை + } else if (hour < 14) { + return ' நண்பகல்'; // நண்பகல் + } else if (hour < 18) { + return ' எற்பாடு'; // எற்பாடு + } else if (hour < 22) { + return ' மாலை'; // மாலை + } else { + return ' யாமம்'; } - }); + }, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === 'யாமம்') { + return hour < 2 ? hour : hour + 12; + } else if (meridiem === 'வைகறை' || meridiem === 'காலை') { + return hour; + } else if (meridiem === 'நண்பகல்') { + return hour >= 10 ? hour : hour + 12; + } else { + return hour + 12; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); - return ta; +return ta; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/te.js b/lib/javascripts/moment_locale/te.js old mode 100755 new mode 100644 index f5d162090e..fe339047f5 --- a/lib/javascripts/moment_locale/te.js +++ b/lib/javascripts/moment_locale/te.js @@ -1,89 +1,89 @@ //! moment.js locale configuration -//! locale : telugu (te) +//! locale : Telugu [te] //! author : Krishna Chaitanya Thota : https://github.com/kcthota ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var te = moment.defineLocale('te', { - months : 'జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జూలై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్'.split('_'), - monthsShort : 'జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జూలై_ఆగ._సెప్._అక్టో._నవ._డిసె.'.split('_'), - monthsParseExact : true, - weekdays : 'ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం'.split('_'), - weekdaysShort : 'ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని'.split('_'), - weekdaysMin : 'ఆ_సో_మం_బు_గు_శు_శ'.split('_'), - longDateFormat : { - LT : 'A h:mm', - LTS : 'A h:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm', - LLLL : 'dddd, D MMMM YYYY, A h:mm' - }, - calendar : { - sameDay : '[నేడు] LT', - nextDay : '[రేపు] LT', - nextWeek : 'dddd, LT', - lastDay : '[నిన్న] LT', - lastWeek : '[గత] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s లో', - past : '%s క్రితం', - s : 'కొన్ని క్షణాలు', - m : 'ఒక నిమిషం', - mm : '%d నిమిషాలు', - h : 'ఒక గంట', - hh : '%d గంటలు', - d : 'ఒక రోజు', - dd : '%d రోజులు', - M : 'ఒక నెల', - MM : '%d నెలలు', - y : 'ఒక సంవత్సరం', - yy : '%d సంవత్సరాలు' - }, - ordinalParse : /\d{1,2}వ/, - ordinal : '%dవ', - meridiemParse: /రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'రాత్రి') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'ఉదయం') { - return hour; - } else if (meridiem === 'మధ్యాహ్నం') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'సాయంత్రం') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'రాత్రి'; - } else if (hour < 10) { - return 'ఉదయం'; - } else if (hour < 17) { - return 'మధ్యాహ్నం'; - } else if (hour < 20) { - return 'సాయంత్రం'; - } else { - return 'రాత్రి'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. +var te = moment.defineLocale('te', { + months : 'జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జూలై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్'.split('_'), + monthsShort : 'జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జూలై_ఆగ._సెప్._అక్టో._నవ._డిసె.'.split('_'), + monthsParseExact : true, + weekdays : 'ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం'.split('_'), + weekdaysShort : 'ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని'.split('_'), + weekdaysMin : 'ఆ_సో_మం_బు_గు_శు_శ'.split('_'), + longDateFormat : { + LT : 'A h:mm', + LTS : 'A h:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY, A h:mm', + LLLL : 'dddd, D MMMM YYYY, A h:mm' + }, + calendar : { + sameDay : '[నేడు] LT', + nextDay : '[రేపు] LT', + nextWeek : 'dddd, LT', + lastDay : '[నిన్న] LT', + lastWeek : '[గత] dddd, LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s లో', + past : '%s క్రితం', + s : 'కొన్ని క్షణాలు', + m : 'ఒక నిమిషం', + mm : '%d నిమిషాలు', + h : 'ఒక గంట', + hh : '%d గంటలు', + d : 'ఒక రోజు', + dd : '%d రోజులు', + M : 'ఒక నెల', + MM : '%d నెలలు', + y : 'ఒక సంవత్సరం', + yy : '%d సంవత్సరాలు' + }, + dayOfMonthOrdinalParse : /\d{1,2}వ/, + ordinal : '%dవ', + meridiemParse: /రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; } - }); + if (meridiem === 'రాత్రి') { + return hour < 4 ? hour : hour + 12; + } else if (meridiem === 'ఉదయం') { + return hour; + } else if (meridiem === 'మధ్యాహ్నం') { + return hour >= 10 ? hour : hour + 12; + } else if (meridiem === 'సాయంత్రం') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'రాత్రి'; + } else if (hour < 10) { + return 'ఉదయం'; + } else if (hour < 17) { + return 'మధ్యాహ్నం'; + } else if (hour < 20) { + return 'సాయంత్రం'; + } else { + return 'రాత్రి'; + } + }, + week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + } +}); - return te; +return te; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/tet.js b/lib/javascripts/moment_locale/tet.js new file mode 100644 index 0000000000..c9c2928f5e --- /dev/null +++ b/lib/javascripts/moment_locale/tet.js @@ -0,0 +1,68 @@ +//! moment.js locale configuration +//! locale : Tetun Dili (East Timor) [tet] +//! author : Joshua Brooks : https://github.com/joshbrooks +//! author : Onorio De J. Afonso : https://github.com/marobo + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var tet = moment.defineLocale('tet', { + months : 'Janeiru_Fevereiru_Marsu_Abril_Maiu_Juniu_Juliu_Augustu_Setembru_Outubru_Novembru_Dezembru'.split('_'), + monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Aug_Set_Out_Nov_Dez'.split('_'), + weekdays : 'Domingu_Segunda_Tersa_Kuarta_Kinta_Sexta_Sabadu'.split('_'), + weekdaysShort : 'Dom_Seg_Ters_Kua_Kint_Sext_Sab'.split('_'), + weekdaysMin : 'Do_Seg_Te_Ku_Ki_Sex_Sa'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[Ohin iha] LT', + nextDay: '[Aban iha] LT', + nextWeek: 'dddd [iha] LT', + lastDay: '[Horiseik iha] LT', + lastWeek: 'dddd [semana kotuk] [iha] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'iha %s', + past : '%s liuba', + s : 'minutu balun', + m : 'minutu ida', + mm : 'minutus %d', + h : 'horas ida', + hh : 'horas %d', + d : 'loron ida', + dd : 'loron %d', + M : 'fulan ida', + MM : 'fulan %d', + y : 'tinan ida', + yy : 'tinan %d' + }, + dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return tet; + +}))); diff --git a/lib/javascripts/moment_locale/th.js b/lib/javascripts/moment_locale/th.js old mode 100755 new mode 100644 index fc4e8f8b74..17ec0f1902 --- a/lib/javascripts/moment_locale/th.js +++ b/lib/javascripts/moment_locale/th.js @@ -1,67 +1,67 @@ //! moment.js locale configuration -//! locale : thai (th) +//! locale : Thai [th] //! author : Kridsada Thanabulpong : https://github.com/sirn ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var th = moment.defineLocale('th', { - months : 'มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม'.split('_'), - monthsShort : 'มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา'.split('_'), - monthsParseExact: true, - weekdays : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์'.split('_'), - weekdaysShort : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์'.split('_'), // yes, three characters difference - weekdaysMin : 'อา._จ._อ._พ._พฤ._ศ._ส.'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H นาฬิกา m นาที', - LTS : 'H นาฬิกา m นาที s วินาที', - L : 'YYYY/MM/DD', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY เวลา H นาฬิกา m นาที', - LLLL : 'วันddddที่ D MMMM YYYY เวลา H นาฬิกา m นาที' - }, - meridiemParse: /ก่อนเที่ยง|หลังเที่ยง/, - isPM: function (input) { - return input === 'หลังเที่ยง'; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ก่อนเที่ยง'; - } else { - return 'หลังเที่ยง'; - } - }, - calendar : { - sameDay : '[วันนี้ เวลา] LT', - nextDay : '[พรุ่งนี้ เวลา] LT', - nextWeek : 'dddd[หน้า เวลา] LT', - lastDay : '[เมื่อวานนี้ เวลา] LT', - lastWeek : '[วัน]dddd[ที่แล้ว เวลา] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'อีก %s', - past : '%sที่แล้ว', - s : 'ไม่กี่วินาที', - m : '1 นาที', - mm : '%d นาที', - h : '1 ชั่วโมง', - hh : '%d ชั่วโมง', - d : '1 วัน', - dd : '%d วัน', - M : '1 เดือน', - MM : '%d เดือน', - y : '1 ปี', - yy : '%d ปี' +var th = moment.defineLocale('th', { + months : 'มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม'.split('_'), + monthsShort : 'ม.ค._ก.พ._มี.ค._เม.ย._พ.ค._มิ.ย._ก.ค._ส.ค._ก.ย._ต.ค._พ.ย._ธ.ค.'.split('_'), + monthsParseExact: true, + weekdays : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์'.split('_'), + weekdaysShort : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์'.split('_'), // yes, three characters difference + weekdaysMin : 'อา._จ._อ._พ._พฤ._ศ._ส.'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY เวลา H:mm', + LLLL : 'วันddddที่ D MMMM YYYY เวลา H:mm' + }, + meridiemParse: /ก่อนเที่ยง|หลังเที่ยง/, + isPM: function (input) { + return input === 'หลังเที่ยง'; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'ก่อนเที่ยง'; + } else { + return 'หลังเที่ยง'; } - }); + }, + calendar : { + sameDay : '[วันนี้ เวลา] LT', + nextDay : '[พรุ่งนี้ เวลา] LT', + nextWeek : 'dddd[หน้า เวลา] LT', + lastDay : '[เมื่อวานนี้ เวลา] LT', + lastWeek : '[วัน]dddd[ที่แล้ว เวลา] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'อีก %s', + past : '%sที่แล้ว', + s : 'ไม่กี่วินาที', + m : '1 นาที', + mm : '%d นาที', + h : '1 ชั่วโมง', + hh : '%d ชั่วโมง', + d : '1 วัน', + dd : '%d วัน', + M : '1 เดือน', + MM : '%d เดือน', + y : '1 ปี', + yy : '%d ปี' + } +}); - return th; +return th; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/tl-ph.js b/lib/javascripts/moment_locale/tl-ph.js old mode 100755 new mode 100644 index d101fd9256..f66b022073 --- a/lib/javascripts/moment_locale/tl-ph.js +++ b/lib/javascripts/moment_locale/tl-ph.js @@ -1,62 +1,62 @@ //! moment.js locale configuration -//! locale : Tagalog/Filipino (tl-ph) -//! author : Dan Hagman +//! locale : Tagalog (Philippines) [tl-ph] +//! author : Dan Hagman : https://github.com/hagmandan ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var tl_ph = moment.defineLocale('tl-ph', { - months : 'Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre'.split('_'), - monthsShort : 'Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis'.split('_'), - weekdays : 'Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado'.split('_'), - weekdaysShort : 'Lin_Lun_Mar_Miy_Huw_Biy_Sab'.split('_'), - weekdaysMin : 'Li_Lu_Ma_Mi_Hu_Bi_Sab'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'MM/D/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY HH:mm', - LLLL : 'dddd, MMMM DD, YYYY HH:mm' - }, - calendar : { - sameDay: '[Ngayon sa] LT', - nextDay: '[Bukas sa] LT', - nextWeek: 'dddd [sa] LT', - lastDay: '[Kahapon sa] LT', - lastWeek: 'dddd [huling linggo] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'sa loob ng %s', - past : '%s ang nakalipas', - s : 'ilang segundo', - m : 'isang minuto', - mm : '%d minuto', - h : 'isang oras', - hh : '%d oras', - d : 'isang araw', - dd : '%d araw', - M : 'isang buwan', - MM : '%d buwan', - y : 'isang taon', - yy : '%d taon' - }, - ordinalParse: /\d{1,2}/, - ordinal : function (number) { - return number; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var tlPh = moment.defineLocale('tl-ph', { + months : 'Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre'.split('_'), + monthsShort : 'Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis'.split('_'), + weekdays : 'Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado'.split('_'), + weekdaysShort : 'Lin_Lun_Mar_Miy_Huw_Biy_Sab'.split('_'), + weekdaysMin : 'Li_Lu_Ma_Mi_Hu_Bi_Sab'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'MM/D/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY HH:mm', + LLLL : 'dddd, MMMM DD, YYYY HH:mm' + }, + calendar : { + sameDay: 'LT [ngayong araw]', + nextDay: '[Bukas ng] LT', + nextWeek: 'LT [sa susunod na] dddd', + lastDay: 'LT [kahapon]', + lastWeek: 'LT [noong nakaraang] dddd', + sameElse: 'L' + }, + relativeTime : { + future : 'sa loob ng %s', + past : '%s ang nakalipas', + s : 'ilang segundo', + m : 'isang minuto', + mm : '%d minuto', + h : 'isang oras', + hh : '%d oras', + d : 'isang araw', + dd : '%d araw', + M : 'isang buwan', + MM : '%d buwan', + y : 'isang taon', + yy : '%d taon' + }, + dayOfMonthOrdinalParse: /\d{1,2}/, + ordinal : function (number) { + return number; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return tl_ph; +return tlPh; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/tlh.js b/lib/javascripts/moment_locale/tlh.js old mode 100755 new mode 100644 index 660d70d1c7..7c3123c659 --- a/lib/javascripts/moment_locale/tlh.js +++ b/lib/javascripts/moment_locale/tlh.js @@ -1,120 +1,120 @@ //! moment.js locale configuration -//! locale : Klingon (tlh) +//! locale : Klingon [tlh] //! author : Dominika Kruk : https://github.com/amaranthrose ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var numbersNouns = 'pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut'.split('_'); +var numbersNouns = 'pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut'.split('_'); - function translateFuture(output) { - var time = output; - time = (output.indexOf('jaj') !== -1) ? - time.slice(0, -3) + 'leS' : - (output.indexOf('jar') !== -1) ? - time.slice(0, -3) + 'waQ' : - (output.indexOf('DIS') !== -1) ? - time.slice(0, -3) + 'nem' : - time + ' pIq'; - return time; +function translateFuture(output) { + var time = output; + time = (output.indexOf('jaj') !== -1) ? + time.slice(0, -3) + 'leS' : + (output.indexOf('jar') !== -1) ? + time.slice(0, -3) + 'waQ' : + (output.indexOf('DIS') !== -1) ? + time.slice(0, -3) + 'nem' : + time + ' pIq'; + return time; +} + +function translatePast(output) { + var time = output; + time = (output.indexOf('jaj') !== -1) ? + time.slice(0, -3) + 'Hu’' : + (output.indexOf('jar') !== -1) ? + time.slice(0, -3) + 'wen' : + (output.indexOf('DIS') !== -1) ? + time.slice(0, -3) + 'ben' : + time + ' ret'; + return time; +} + +function translate(number, withoutSuffix, string, isFuture) { + var numberNoun = numberAsNoun(number); + switch (string) { + case 'mm': + return numberNoun + ' tup'; + case 'hh': + return numberNoun + ' rep'; + case 'dd': + return numberNoun + ' jaj'; + case 'MM': + return numberNoun + ' jar'; + case 'yy': + return numberNoun + ' DIS'; } +} - function translatePast(output) { - var time = output; - time = (output.indexOf('jaj') !== -1) ? - time.slice(0, -3) + 'Hu’' : - (output.indexOf('jar') !== -1) ? - time.slice(0, -3) + 'wen' : - (output.indexOf('DIS') !== -1) ? - time.slice(0, -3) + 'ben' : - time + ' ret'; - return time; +function numberAsNoun(number) { + var hundred = Math.floor((number % 1000) / 100), + ten = Math.floor((number % 100) / 10), + one = number % 10, + word = ''; + if (hundred > 0) { + word += numbersNouns[hundred] + 'vatlh'; } - - function translate(number, withoutSuffix, string, isFuture) { - var numberNoun = numberAsNoun(number); - switch (string) { - case 'mm': - return numberNoun + ' tup'; - case 'hh': - return numberNoun + ' rep'; - case 'dd': - return numberNoun + ' jaj'; - case 'MM': - return numberNoun + ' jar'; - case 'yy': - return numberNoun + ' DIS'; - } + if (ten > 0) { + word += ((word !== '') ? ' ' : '') + numbersNouns[ten] + 'maH'; } - - function numberAsNoun(number) { - var hundred = Math.floor((number % 1000) / 100), - ten = Math.floor((number % 100) / 10), - one = number % 10, - word = ''; - if (hundred > 0) { - word += numbersNouns[hundred] + 'vatlh'; - } - if (ten > 0) { - word += ((word !== '') ? ' ' : '') + numbersNouns[ten] + 'maH'; - } - if (one > 0) { - word += ((word !== '') ? ' ' : '') + numbersNouns[one]; - } - return (word === '') ? 'pagh' : word; + if (one > 0) { + word += ((word !== '') ? ' ' : '') + numbersNouns[one]; } + return (word === '') ? 'pagh' : word; +} - var tlh = moment.defineLocale('tlh', { - months : 'tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’'.split('_'), - monthsShort : 'jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’'.split('_'), - monthsParseExact : true, - weekdays : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), - weekdaysShort : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), - weekdaysMin : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[DaHjaj] LT', - nextDay: '[wa’leS] LT', - nextWeek: 'LLL', - lastDay: '[wa’Hu’] LT', - lastWeek: 'LLL', - sameElse: 'L' - }, - relativeTime : { - future : translateFuture, - past : translatePast, - s : 'puS lup', - m : 'wa’ tup', - mm : translate, - h : 'wa’ rep', - hh : translate, - d : 'wa’ jaj', - dd : translate, - M : 'wa’ jar', - MM : translate, - y : 'wa’ DIS', - yy : translate - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); +var tlh = moment.defineLocale('tlh', { + months : 'tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’'.split('_'), + monthsShort : 'jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’'.split('_'), + monthsParseExact : true, + weekdays : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), + weekdaysShort : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), + weekdaysMin : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[DaHjaj] LT', + nextDay: '[wa’leS] LT', + nextWeek: 'LLL', + lastDay: '[wa’Hu’] LT', + lastWeek: 'LLL', + sameElse: 'L' + }, + relativeTime : { + future : translateFuture, + past : translatePast, + s : 'puS lup', + m : 'wa’ tup', + mm : translate, + h : 'wa’ rep', + hh : translate, + d : 'wa’ jaj', + dd : translate, + M : 'wa’ jar', + MM : translate, + y : 'wa’ DIS', + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return tlh; +return tlh; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/tr.js b/lib/javascripts/moment_locale/tr.js old mode 100755 new mode 100644 index 638edbb025..c4c58ca66f --- a/lib/javascripts/moment_locale/tr.js +++ b/lib/javascripts/moment_locale/tr.js @@ -1,90 +1,90 @@ //! moment.js locale configuration -//! locale : turkish (tr) +//! locale : Turkish [tr] //! authors : Erhan Gundogan : https://github.com/erhangundogan, //! Burak Yiğit Kaya: https://github.com/BYK ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var suffixes = { - 1: '\'inci', - 5: '\'inci', - 8: '\'inci', - 70: '\'inci', - 80: '\'inci', - 2: '\'nci', - 7: '\'nci', - 20: '\'nci', - 50: '\'nci', - 3: '\'üncü', - 4: '\'üncü', - 100: '\'üncü', - 6: '\'ncı', - 9: '\'uncu', - 10: '\'uncu', - 30: '\'uncu', - 60: '\'ıncı', - 90: '\'ıncı' - }; +var suffixes = { + 1: '\'inci', + 5: '\'inci', + 8: '\'inci', + 70: '\'inci', + 80: '\'inci', + 2: '\'nci', + 7: '\'nci', + 20: '\'nci', + 50: '\'nci', + 3: '\'üncü', + 4: '\'üncü', + 100: '\'üncü', + 6: '\'ncı', + 9: '\'uncu', + 10: '\'uncu', + 30: '\'uncu', + 60: '\'ıncı', + 90: '\'ıncı' +}; - var tr = moment.defineLocale('tr', { - months : 'Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık'.split('_'), - monthsShort : 'Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara'.split('_'), - weekdays : 'Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi'.split('_'), - weekdaysShort : 'Paz_Pts_Sal_Çar_Per_Cum_Cts'.split('_'), - weekdaysMin : 'Pz_Pt_Sa_Ça_Pe_Cu_Ct'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[bugün saat] LT', - nextDay : '[yarın saat] LT', - nextWeek : '[haftaya] dddd [saat] LT', - lastDay : '[dün] LT', - lastWeek : '[geçen hafta] dddd [saat] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s sonra', - past : '%s önce', - s : 'birkaç saniye', - m : 'bir dakika', - mm : '%d dakika', - h : 'bir saat', - hh : '%d saat', - d : 'bir gün', - dd : '%d gün', - M : 'bir ay', - MM : '%d ay', - y : 'bir yıl', - yy : '%d yıl' - }, - ordinalParse: /\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/, - ordinal : function (number) { - if (number === 0) { // special case for zero - return number + '\'ıncı'; - } - var a = number % 10, - b = number % 100 - a, - c = number >= 100 ? 100 : null; - return number + (suffixes[a] || suffixes[b] || suffixes[c]); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. +var tr = moment.defineLocale('tr', { + months : 'Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık'.split('_'), + monthsShort : 'Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara'.split('_'), + weekdays : 'Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi'.split('_'), + weekdaysShort : 'Paz_Pts_Sal_Çar_Per_Cum_Cts'.split('_'), + weekdaysMin : 'Pz_Pt_Sa_Ça_Pe_Cu_Ct'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[bugün saat] LT', + nextDay : '[yarın saat] LT', + nextWeek : '[haftaya] dddd [saat] LT', + lastDay : '[dün] LT', + lastWeek : '[geçen hafta] dddd [saat] LT', + sameElse : 'L' + }, + relativeTime : { + future : '%s sonra', + past : '%s önce', + s : 'birkaç saniye', + m : 'bir dakika', + mm : '%d dakika', + h : 'bir saat', + hh : '%d saat', + d : 'bir gün', + dd : '%d gün', + M : 'bir ay', + MM : '%d ay', + y : 'bir yıl', + yy : '%d yıl' + }, + dayOfMonthOrdinalParse: /\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/, + ordinal : function (number) { + if (number === 0) { // special case for zero + return number + '\'ıncı'; } - }); + var a = number % 10, + b = number % 100 - a, + c = number >= 100 ? 100 : null; + return number + (suffixes[a] || suffixes[b] || suffixes[c]); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return tr; +return tr; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/tzl.js b/lib/javascripts/moment_locale/tzl.js old mode 100755 new mode 100644 index ddbbf01793..978fcb488e --- a/lib/javascripts/moment_locale/tzl.js +++ b/lib/javascripts/moment_locale/tzl.js @@ -1,91 +1,91 @@ //! moment.js locale configuration -//! locale : talossan (tzl) -//! author : Robin van der Vliet : https://github.com/robin0van0der0v with the help of Iustì Canun +//! locale : Talossan [tzl] +//! author : Robin van der Vliet : https://github.com/robin0van0der0v +//! author : Iustì Canun ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - - // After the year there should be a slash and the amount of years since December 26, 1979 in Roman numerals. - // This is currently too difficult (maybe even impossible) to add. - var tzl = moment.defineLocale('tzl', { - months : 'Januar_Fevraglh_Març_Avrïu_Mai_Gün_Julia_Guscht_Setemvar_Listopäts_Noemvar_Zecemvar'.split('_'), - monthsShort : 'Jan_Fev_Mar_Avr_Mai_Gün_Jul_Gus_Set_Lis_Noe_Zec'.split('_'), - weekdays : 'Súladi_Lúneçi_Maitzi_Márcuri_Xhúadi_Viénerçi_Sáturi'.split('_'), - weekdaysShort : 'Súl_Lún_Mai_Már_Xhú_Vié_Sát'.split('_'), - weekdaysMin : 'Sú_Lú_Ma_Má_Xh_Vi_Sá'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM [dallas] YYYY', - LLL : 'D. MMMM [dallas] YYYY HH.mm', - LLLL : 'dddd, [li] D. MMMM [dallas] YYYY HH.mm' - }, - meridiemParse: /d\'o|d\'a/i, - isPM : function (input) { - return 'd\'o' === input.toLowerCase(); - }, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'd\'o' : 'D\'O'; - } else { - return isLower ? 'd\'a' : 'D\'A'; - } - }, - calendar : { - sameDay : '[oxhi à] LT', - nextDay : '[demà à] LT', - nextWeek : 'dddd [à] LT', - lastDay : '[ieiri à] LT', - lastWeek : '[sür el] dddd [lasteu à] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'osprei %s', - past : 'ja%s', - s : processRelativeTime, - m : processRelativeTime, - mm : processRelativeTime, - h : processRelativeTime, - hh : processRelativeTime, - d : processRelativeTime, - dd : processRelativeTime, - M : processRelativeTime, - MM : processRelativeTime, - y : processRelativeTime, - yy : processRelativeTime - }, - ordinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. +// After the year there should be a slash and the amount of years since December 26, 1979 in Roman numerals. +// This is currently too difficult (maybe even impossible) to add. +var tzl = moment.defineLocale('tzl', { + months : 'Januar_Fevraglh_Març_Avrïu_Mai_Gün_Julia_Guscht_Setemvar_Listopäts_Noemvar_Zecemvar'.split('_'), + monthsShort : 'Jan_Fev_Mar_Avr_Mai_Gün_Jul_Gus_Set_Lis_Noe_Zec'.split('_'), + weekdays : 'Súladi_Lúneçi_Maitzi_Márcuri_Xhúadi_Viénerçi_Sáturi'.split('_'), + weekdaysShort : 'Súl_Lún_Mai_Már_Xhú_Vié_Sát'.split('_'), + weekdaysMin : 'Sú_Lú_Ma_Má_Xh_Vi_Sá'.split('_'), + longDateFormat : { + LT : 'HH.mm', + LTS : 'HH.mm.ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM [dallas] YYYY', + LLL : 'D. MMMM [dallas] YYYY HH.mm', + LLLL : 'dddd, [li] D. MMMM [dallas] YYYY HH.mm' + }, + meridiemParse: /d\'o|d\'a/i, + isPM : function (input) { + return 'd\'o' === input.toLowerCase(); + }, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'd\'o' : 'D\'O'; + } else { + return isLower ? 'd\'a' : 'D\'A'; } - }); - - function processRelativeTime(number, withoutSuffix, key, isFuture) { - var format = { - 's': ['viensas secunds', '\'iensas secunds'], - 'm': ['\'n míut', '\'iens míut'], - 'mm': [number + ' míuts', '' + number + ' míuts'], - 'h': ['\'n þora', '\'iensa þora'], - 'hh': [number + ' þoras', '' + number + ' þoras'], - 'd': ['\'n ziua', '\'iensa ziua'], - 'dd': [number + ' ziuas', '' + number + ' ziuas'], - 'M': ['\'n mes', '\'iens mes'], - 'MM': [number + ' mesen', '' + number + ' mesen'], - 'y': ['\'n ar', '\'iens ar'], - 'yy': [number + ' ars', '' + number + ' ars'] - }; - return isFuture ? format[key][0] : (withoutSuffix ? format[key][0] : format[key][1]); + }, + calendar : { + sameDay : '[oxhi à] LT', + nextDay : '[demà à] LT', + nextWeek : 'dddd [à] LT', + lastDay : '[ieiri à] LT', + lastWeek : '[sür el] dddd [lasteu à] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'osprei %s', + past : 'ja%s', + s : processRelativeTime, + m : processRelativeTime, + mm : processRelativeTime, + h : processRelativeTime, + hh : processRelativeTime, + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } +}); - return tzl; +function processRelativeTime(number, withoutSuffix, key, isFuture) { + var format = { + 's': ['viensas secunds', '\'iensas secunds'], + 'm': ['\'n míut', '\'iens míut'], + 'mm': [number + ' míuts', '' + number + ' míuts'], + 'h': ['\'n þora', '\'iensa þora'], + 'hh': [number + ' þoras', '' + number + ' þoras'], + 'd': ['\'n ziua', '\'iensa ziua'], + 'dd': [number + ' ziuas', '' + number + ' ziuas'], + 'M': ['\'n mes', '\'iens mes'], + 'MM': [number + ' mesen', '' + number + ' mesen'], + 'y': ['\'n ar', '\'iens ar'], + 'yy': [number + ' ars', '' + number + ' ars'] + }; + return isFuture ? format[key][0] : (withoutSuffix ? format[key][0] : format[key][1]); +} -})); \ No newline at end of file +return tzl; + +}))); diff --git a/lib/javascripts/moment_locale/tzm-latn.js b/lib/javascripts/moment_locale/tzm-latn.js old mode 100755 new mode 100644 index 712f5f5e41..4d742c52fe --- a/lib/javascripts/moment_locale/tzm-latn.js +++ b/lib/javascripts/moment_locale/tzm-latn.js @@ -1,58 +1,58 @@ //! moment.js locale configuration -//! locale : Morocco Central Atlas Tamaziɣt in Latin (tzm-latn) +//! locale : Central Atlas Tamazight Latin [tzm-latn] //! author : Abdel Said : https://github.com/abdelsaid ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var tzm_latn = moment.defineLocale('tzm-latn', { - months : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'), - monthsShort : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'), - weekdays : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), - weekdaysShort : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), - weekdaysMin : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[asdkh g] LT', - nextDay: '[aska g] LT', - nextWeek: 'dddd [g] LT', - lastDay: '[assant g] LT', - lastWeek: 'dddd [g] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'dadkh s yan %s', - past : 'yan %s', - s : 'imik', - m : 'minuḍ', - mm : '%d minuḍ', - h : 'saɛa', - hh : '%d tassaɛin', - d : 'ass', - dd : '%d ossan', - M : 'ayowr', - MM : '%d iyyirn', - y : 'asgas', - yy : '%d isgasn' - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); +var tzmLatn = moment.defineLocale('tzm-latn', { + months : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'), + monthsShort : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'), + weekdays : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), + weekdaysShort : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), + weekdaysMin : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[asdkh g] LT', + nextDay: '[aska g] LT', + nextWeek: 'dddd [g] LT', + lastDay: '[assant g] LT', + lastWeek: 'dddd [g] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'dadkh s yan %s', + past : 'yan %s', + s : 'imik', + m : 'minuḍ', + mm : '%d minuḍ', + h : 'saɛa', + hh : '%d tassaɛin', + d : 'ass', + dd : '%d ossan', + M : 'ayowr', + MM : '%d iyyirn', + y : 'asgas', + yy : '%d isgasn' + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); - return tzm_latn; +return tzmLatn; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/tzm.js b/lib/javascripts/moment_locale/tzm.js old mode 100755 new mode 100644 index 6b8acc08ee..1d1c26006d --- a/lib/javascripts/moment_locale/tzm.js +++ b/lib/javascripts/moment_locale/tzm.js @@ -1,58 +1,58 @@ //! moment.js locale configuration -//! locale : Morocco Central Atlas Tamaziɣt (tzm) +//! locale : Central Atlas Tamazight [tzm] //! author : Abdel Said : https://github.com/abdelsaid ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var tzm = moment.defineLocale('tzm', { - months : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'), - monthsShort : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'), - weekdays : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), - weekdaysShort : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), - weekdaysMin : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS: 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[ⴰⵙⴷⵅ ⴴ] LT', - nextDay: '[ⴰⵙⴽⴰ ⴴ] LT', - nextWeek: 'dddd [ⴴ] LT', - lastDay: '[ⴰⵚⴰⵏⵜ ⴴ] LT', - lastWeek: 'dddd [ⴴ] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s', - past : 'ⵢⴰⵏ %s', - s : 'ⵉⵎⵉⴽ', - m : 'ⵎⵉⵏⵓⴺ', - mm : '%d ⵎⵉⵏⵓⴺ', - h : 'ⵙⴰⵄⴰ', - hh : '%d ⵜⴰⵙⵙⴰⵄⵉⵏ', - d : 'ⴰⵙⵙ', - dd : '%d oⵙⵙⴰⵏ', - M : 'ⴰⵢoⵓⵔ', - MM : '%d ⵉⵢⵢⵉⵔⵏ', - y : 'ⴰⵙⴳⴰⵙ', - yy : '%d ⵉⵙⴳⴰⵙⵏ' - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); +var tzm = moment.defineLocale('tzm', { + months : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'), + monthsShort : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'), + weekdays : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), + weekdaysShort : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), + weekdaysMin : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS: 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[ⴰⵙⴷⵅ ⴴ] LT', + nextDay: '[ⴰⵙⴽⴰ ⴴ] LT', + nextWeek: 'dddd [ⴴ] LT', + lastDay: '[ⴰⵚⴰⵏⵜ ⴴ] LT', + lastWeek: 'dddd [ⴴ] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s', + past : 'ⵢⴰⵏ %s', + s : 'ⵉⵎⵉⴽ', + m : 'ⵎⵉⵏⵓⴺ', + mm : '%d ⵎⵉⵏⵓⴺ', + h : 'ⵙⴰⵄⴰ', + hh : '%d ⵜⴰⵙⵙⴰⵄⵉⵏ', + d : 'ⴰⵙⵙ', + dd : '%d oⵙⵙⴰⵏ', + M : 'ⴰⵢoⵓⵔ', + MM : '%d ⵉⵢⵢⵉⵔⵏ', + y : 'ⴰⵙⴳⴰⵙ', + yy : '%d ⵉⵙⴳⴰⵙⵏ' + }, + week : { + dow : 6, // Saturday is the first day of the week. + doy : 12 // The week that contains Jan 1st is the first week of the year. + } +}); - return tzm; +return tzm; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/uk.js b/lib/javascripts/moment_locale/uk.js old mode 100755 new mode 100644 index 80c34973c5..1aff8c14d7 --- a/lib/javascripts/moment_locale/uk.js +++ b/lib/javascripts/moment_locale/uk.js @@ -1,81 +1,86 @@ //! moment.js locale configuration -//! locale : ukrainian (uk) +//! locale : Ukrainian [uk] //! author : zemlanin : https://github.com/zemlanin //! Author : Menelion Elensúle : https://github.com/Oire ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - function plural(word, num) { - var forms = word.split('_'); - return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +function plural(word, num) { + var forms = word.split('_'); + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +} +function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': withoutSuffix ? 'хвилина_хвилини_хвилин' : 'хвилину_хвилини_хвилин', + 'hh': withoutSuffix ? 'година_години_годин' : 'годину_години_годин', + 'dd': 'день_дні_днів', + 'MM': 'місяць_місяці_місяців', + 'yy': 'рік_роки_років' + }; + if (key === 'm') { + return withoutSuffix ? 'хвилина' : 'хвилину'; } - function relativeTimeWithPlural(number, withoutSuffix, key) { - var format = { - 'mm': withoutSuffix ? 'хвилина_хвилини_хвилин' : 'хвилину_хвилини_хвилин', - 'hh': withoutSuffix ? 'година_години_годин' : 'годину_години_годин', - 'dd': 'день_дні_днів', - 'MM': 'місяць_місяці_місяців', - 'yy': 'рік_роки_років' - }; - if (key === 'm') { - return withoutSuffix ? 'хвилина' : 'хвилину'; - } - else if (key === 'h') { - return withoutSuffix ? 'година' : 'годину'; - } - else { - return number + ' ' + plural(format[key], +number); - } + else if (key === 'h') { + return withoutSuffix ? 'година' : 'годину'; } - function weekdaysCaseReplace(m, format) { - var weekdays = { - 'nominative': 'неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота'.split('_'), - 'accusative': 'неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу'.split('_'), - 'genitive': 'неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи'.split('_') - }, - nounCase = (/(\[[ВвУу]\]) ?dddd/).test(format) ? - 'accusative' : - ((/\[?(?:минулої|наступної)? ?\] ?dddd/).test(format) ? - 'genitive' : - 'nominative'); - return weekdays[nounCase][m.day()]; + else { + return number + ' ' + plural(format[key], +number); } - function processHoursFunction(str) { - return function () { - return str + 'о' + (this.hours() === 11 ? 'б' : '') + '] LT'; - }; +} +function weekdaysCaseReplace(m, format) { + var weekdays = { + 'nominative': 'неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота'.split('_'), + 'accusative': 'неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу'.split('_'), + 'genitive': 'неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи'.split('_') + }; + + if (!m) { + return weekdays['nominative']; } - var uk = moment.defineLocale('uk', { - months : { - 'format': 'січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня'.split('_'), - 'standalone': 'січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень'.split('_') - }, - monthsShort : 'січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд'.split('_'), - weekdays : weekdaysCaseReplace, - weekdaysShort : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), - weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY р.', - LLL : 'D MMMM YYYY р., HH:mm', - LLLL : 'dddd, D MMMM YYYY р., HH:mm' - }, - calendar : { - sameDay: processHoursFunction('[Сьогодні '), - nextDay: processHoursFunction('[Завтра '), - lastDay: processHoursFunction('[Вчора '), - nextWeek: processHoursFunction('[У] dddd ['), - lastWeek: function () { - switch (this.day()) { + var nounCase = (/(\[[ВвУу]\]) ?dddd/).test(format) ? + 'accusative' : + ((/\[?(?:минулої|наступної)? ?\] ?dddd/).test(format) ? + 'genitive' : + 'nominative'); + return weekdays[nounCase][m.day()]; +} +function processHoursFunction(str) { + return function () { + return str + 'о' + (this.hours() === 11 ? 'б' : '') + '] LT'; + }; +} + +var uk = moment.defineLocale('uk', { + months : { + 'format': 'січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня'.split('_'), + 'standalone': 'січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень'.split('_') + }, + monthsShort : 'січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд'.split('_'), + weekdays : weekdaysCaseReplace, + weekdaysShort : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), + weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D MMMM YYYY р.', + LLL : 'D MMMM YYYY р., HH:mm', + LLLL : 'dddd, D MMMM YYYY р., HH:mm' + }, + calendar : { + sameDay: processHoursFunction('[Сьогодні '), + nextDay: processHoursFunction('[Завтра '), + lastDay: processHoursFunction('[Вчора '), + nextWeek: processHoursFunction('[У] dddd ['), + lastWeek: function () { + switch (this.day()) { case 0: case 3: case 5: @@ -85,44 +90,44 @@ case 2: case 4: return processHoursFunction('[Минулого] dddd [').call(this); - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'за %s', - past : '%s тому', - s : 'декілька секунд', - m : relativeTimeWithPlural, - mm : relativeTimeWithPlural, - h : 'годину', - hh : relativeTimeWithPlural, - d : 'день', - dd : relativeTimeWithPlural, - M : 'місяць', - MM : relativeTimeWithPlural, - y : 'рік', - yy : relativeTimeWithPlural - }, - // M. E.: those two are virtually unused but a user might want to implement them for his/her website for some reason - meridiemParse: /ночі|ранку|дня|вечора/, - isPM: function (input) { - return /^(дня|вечора)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ночі'; - } else if (hour < 12) { - return 'ранку'; - } else if (hour < 17) { - return 'дня'; - } else { - return 'вечора'; } }, - ordinalParse: /\d{1,2}-(й|го)/, - ordinal: function (number, period) { - switch (period) { + sameElse: 'L' + }, + relativeTime : { + future : 'за %s', + past : '%s тому', + s : 'декілька секунд', + m : relativeTimeWithPlural, + mm : relativeTimeWithPlural, + h : 'годину', + hh : relativeTimeWithPlural, + d : 'день', + dd : relativeTimeWithPlural, + M : 'місяць', + MM : relativeTimeWithPlural, + y : 'рік', + yy : relativeTimeWithPlural + }, + // M. E.: those two are virtually unused but a user might want to implement them for his/her website for some reason + meridiemParse: /ночі|ранку|дня|вечора/, + isPM: function (input) { + return /^(дня|вечора)$/.test(input); + }, + meridiem : function (hour, minute, isLower) { + if (hour < 4) { + return 'ночі'; + } else if (hour < 12) { + return 'ранку'; + } else if (hour < 17) { + return 'дня'; + } else { + return 'вечора'; + } + }, + dayOfMonthOrdinalParse: /\d{1,2}-(й|го)/, + ordinal: function (number, period) { + switch (period) { case 'M': case 'd': case 'DDD': @@ -133,14 +138,14 @@ return number + '-го'; default: return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. } - }); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); - return uk; +return uk; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/ur.js b/lib/javascripts/moment_locale/ur.js index 8d2111fd03..4cf1c07af9 100644 --- a/lib/javascripts/moment_locale/ur.js +++ b/lib/javascripts/moment_locale/ur.js @@ -1,6 +1,7 @@ //! moment.js locale configuration //! locale : Urdu [ur] //! author : Sawood Alam : https://github.com/ibnesayeed +//! author : Zack : https://github.com/ZackVision ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' diff --git a/lib/javascripts/moment_locale/uz-latn.js b/lib/javascripts/moment_locale/uz-latn.js new file mode 100644 index 0000000000..21463b7668 --- /dev/null +++ b/lib/javascripts/moment_locale/uz-latn.js @@ -0,0 +1,58 @@ +//! moment.js locale configuration +//! locale : Uzbek Latin [uz-latn] +//! author : Rasulbek Mirzayev : github.com/Rasulbeeek + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var uzLatn = moment.defineLocale('uz-latn', { + months : 'Yanvar_Fevral_Mart_Aprel_May_Iyun_Iyul_Avgust_Sentabr_Oktabr_Noyabr_Dekabr'.split('_'), + monthsShort : 'Yan_Fev_Mar_Apr_May_Iyun_Iyul_Avg_Sen_Okt_Noy_Dek'.split('_'), + weekdays : 'Yakshanba_Dushanba_Seshanba_Chorshanba_Payshanba_Juma_Shanba'.split('_'), + weekdaysShort : 'Yak_Dush_Sesh_Chor_Pay_Jum_Shan'.split('_'), + weekdaysMin : 'Ya_Du_Se_Cho_Pa_Ju_Sha'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'D MMMM YYYY, dddd HH:mm' + }, + calendar : { + sameDay : '[Bugun soat] LT [da]', + nextDay : '[Ertaga] LT [da]', + nextWeek : 'dddd [kuni soat] LT [da]', + lastDay : '[Kecha soat] LT [da]', + lastWeek : '[O\'tgan] dddd [kuni soat] LT [da]', + sameElse : 'L' + }, + relativeTime : { + future : 'Yaqin %s ichida', + past : 'Bir necha %s oldin', + s : 'soniya', + m : 'bir daqiqa', + mm : '%d daqiqa', + h : 'bir soat', + hh : '%d soat', + d : 'bir kun', + dd : '%d kun', + M : 'bir oy', + MM : '%d oy', + y : 'bir yil', + yy : '%d yil' + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +}); + +return uzLatn; + +}))); diff --git a/lib/javascripts/moment_locale/uz.js b/lib/javascripts/moment_locale/uz.js old mode 100755 new mode 100644 index fcf594e6a7..378461eb00 --- a/lib/javascripts/moment_locale/uz.js +++ b/lib/javascripts/moment_locale/uz.js @@ -1,58 +1,58 @@ //! moment.js locale configuration -//! locale : uzbek (uz) +//! locale : Uzbek [uz] //! author : Sardor Muminov : https://github.com/muminoff ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var uz = moment.defineLocale('uz', { - months : 'январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр'.split('_'), - monthsShort : 'янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек'.split('_'), - weekdays : 'Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба'.split('_'), - weekdaysShort : 'Якш_Душ_Сеш_Чор_Пай_Жум_Шан'.split('_'), - weekdaysMin : 'Як_Ду_Се_Чо_Па_Жу_Ша'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'D MMMM YYYY, dddd HH:mm' - }, - calendar : { - sameDay : '[Бугун соат] LT [да]', - nextDay : '[Эртага] LT [да]', - nextWeek : 'dddd [куни соат] LT [да]', - lastDay : '[Кеча соат] LT [да]', - lastWeek : '[Утган] dddd [куни соат] LT [да]', - sameElse : 'L' - }, - relativeTime : { - future : 'Якин %s ичида', - past : 'Бир неча %s олдин', - s : 'фурсат', - m : 'бир дакика', - mm : '%d дакика', - h : 'бир соат', - hh : '%d соат', - d : 'бир кун', - dd : '%d кун', - M : 'бир ой', - MM : '%d ой', - y : 'бир йил', - yy : '%d йил' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 4th is the first week of the year. - } - }); +var uz = moment.defineLocale('uz', { + months : 'январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр'.split('_'), + monthsShort : 'янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек'.split('_'), + weekdays : 'Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба'.split('_'), + weekdaysShort : 'Якш_Душ_Сеш_Чор_Пай_Жум_Шан'.split('_'), + weekdaysMin : 'Як_Ду_Се_Чо_Па_Жу_Ша'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'D MMMM YYYY, dddd HH:mm' + }, + calendar : { + sameDay : '[Бугун соат] LT [да]', + nextDay : '[Эртага] LT [да]', + nextWeek : 'dddd [куни соат] LT [да]', + lastDay : '[Кеча соат] LT [да]', + lastWeek : '[Утган] dddd [куни соат] LT [да]', + sameElse : 'L' + }, + relativeTime : { + future : 'Якин %s ичида', + past : 'Бир неча %s олдин', + s : 'фурсат', + m : 'бир дакика', + mm : '%d дакика', + h : 'бир соат', + hh : '%d соат', + d : 'бир кун', + dd : '%d кун', + M : 'бир ой', + MM : '%d ой', + y : 'бир йил', + yy : '%d йил' + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 4th is the first week of the year. + } +}); - return uz; +return uz; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/vi.js b/lib/javascripts/moment_locale/vi.js old mode 100755 new mode 100644 index d3ee6ea072..1adacc3a79 --- a/lib/javascripts/moment_locale/vi.js +++ b/lib/javascripts/moment_locale/vi.js @@ -1,79 +1,79 @@ //! moment.js locale configuration -//! locale : vietnamese (vi) +//! locale : Vietnamese [vi] //! author : Bang Nguyen : https://github.com/bangnk ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var vi = moment.defineLocale('vi', { - months : 'tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12'.split('_'), - monthsShort : 'Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12'.split('_'), - monthsParseExact : true, - weekdays : 'chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy'.split('_'), - weekdaysShort : 'CN_T2_T3_T4_T5_T6_T7'.split('_'), - weekdaysMin : 'CN_T2_T3_T4_T5_T6_T7'.split('_'), - weekdaysParseExact : true, - meridiemParse: /sa|ch/i, - isPM : function (input) { - return /^ch$/i.test(input); - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 12) { - return isLower ? 'sa' : 'SA'; - } else { - return isLower ? 'ch' : 'CH'; - } - }, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM [năm] YYYY', - LLL : 'D MMMM [năm] YYYY HH:mm', - LLLL : 'dddd, D MMMM [năm] YYYY HH:mm', - l : 'DD/M/YYYY', - ll : 'D MMM YYYY', - lll : 'D MMM YYYY HH:mm', - llll : 'ddd, D MMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Hôm nay lúc] LT', - nextDay: '[Ngày mai lúc] LT', - nextWeek: 'dddd [tuần tới lúc] LT', - lastDay: '[Hôm qua lúc] LT', - lastWeek: 'dddd [tuần rồi lúc] LT', - sameElse: 'L' - }, - relativeTime : { - future : '%s tới', - past : '%s trước', - s : 'vài giây', - m : 'một phút', - mm : '%d phút', - h : 'một giờ', - hh : '%d giờ', - d : 'một ngày', - dd : '%d ngày', - M : 'một tháng', - MM : '%d tháng', - y : 'một năm', - yy : '%d năm' - }, - ordinalParse: /\d{1,2}/, - ordinal : function (number) { - return number; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. +var vi = moment.defineLocale('vi', { + months : 'tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12'.split('_'), + monthsShort : 'Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12'.split('_'), + monthsParseExact : true, + weekdays : 'chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy'.split('_'), + weekdaysShort : 'CN_T2_T3_T4_T5_T6_T7'.split('_'), + weekdaysMin : 'CN_T2_T3_T4_T5_T6_T7'.split('_'), + weekdaysParseExact : true, + meridiemParse: /sa|ch/i, + isPM : function (input) { + return /^ch$/i.test(input); + }, + meridiem : function (hours, minutes, isLower) { + if (hours < 12) { + return isLower ? 'sa' : 'SA'; + } else { + return isLower ? 'ch' : 'CH'; } - }); + }, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM [năm] YYYY', + LLL : 'D MMMM [năm] YYYY HH:mm', + LLLL : 'dddd, D MMMM [năm] YYYY HH:mm', + l : 'DD/M/YYYY', + ll : 'D MMM YYYY', + lll : 'D MMM YYYY HH:mm', + llll : 'ddd, D MMM YYYY HH:mm' + }, + calendar : { + sameDay: '[Hôm nay lúc] LT', + nextDay: '[Ngày mai lúc] LT', + nextWeek: 'dddd [tuần tới lúc] LT', + lastDay: '[Hôm qua lúc] LT', + lastWeek: 'dddd [tuần rồi lúc] LT', + sameElse: 'L' + }, + relativeTime : { + future : '%s tới', + past : '%s trước', + s : 'vài giây', + m : 'một phút', + mm : '%d phút', + h : 'một giờ', + hh : '%d giờ', + d : 'một ngày', + dd : '%d ngày', + M : 'một tháng', + MM : '%d tháng', + y : 'một năm', + yy : '%d năm' + }, + dayOfMonthOrdinalParse: /\d{1,2}/, + ordinal : function (number) { + return number; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return vi; +return vi; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/x-pseudo.js b/lib/javascripts/moment_locale/x-pseudo.js new file mode 100644 index 0000000000..b906e16df6 --- /dev/null +++ b/lib/javascripts/moment_locale/x-pseudo.js @@ -0,0 +1,68 @@ +//! moment.js locale configuration +//! locale : Pseudo [x-pseudo] +//! author : Andrew Hood : https://github.com/andrewhood125 + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var xPseudo = moment.defineLocale('x-pseudo', { + months : 'J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér'.split('_'), + monthsShort : 'J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc'.split('_'), + monthsParseExact : true, + weekdays : 'S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý'.split('_'), + weekdaysShort : 'S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát'.split('_'), + weekdaysMin : 'S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd, D MMMM YYYY HH:mm' + }, + calendar : { + sameDay : '[T~ódá~ý át] LT', + nextDay : '[T~ómó~rró~w át] LT', + nextWeek : 'dddd [át] LT', + lastDay : '[Ý~ést~érdá~ý át] LT', + lastWeek : '[L~ást] dddd [át] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'í~ñ %s', + past : '%s á~gó', + s : 'á ~féw ~sécó~ñds', + m : 'á ~míñ~úté', + mm : '%d m~íñú~tés', + h : 'á~ñ hó~úr', + hh : '%d h~óúrs', + d : 'á ~dáý', + dd : '%d d~áýs', + M : 'á ~móñ~th', + MM : '%d m~óñt~hs', + y : 'á ~ýéár', + yy : '%d ý~éárs' + }, + dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (~~(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return xPseudo; + +}))); diff --git a/lib/javascripts/moment_locale/yo.js b/lib/javascripts/moment_locale/yo.js new file mode 100644 index 0000000000..ec28a9122b --- /dev/null +++ b/lib/javascripts/moment_locale/yo.js @@ -0,0 +1,60 @@ +//! moment.js locale configuration +//! locale : Yoruba Nigeria [yo] +//! author : Atolagbe Abisoye : https://github.com/andela-batolagbe + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var yo = moment.defineLocale('yo', { + months : 'Sẹ́rẹ́_Èrèlè_Ẹrẹ̀nà_Ìgbé_Èbibi_Òkùdu_Agẹmo_Ògún_Owewe_Ọ̀wàrà_Bélú_Ọ̀pẹ̀̀'.split('_'), + monthsShort : 'Sẹ́r_Èrl_Ẹrn_Ìgb_Èbi_Òkù_Agẹ_Ògú_Owe_Ọ̀wà_Bél_Ọ̀pẹ̀̀'.split('_'), + weekdays : 'Àìkú_Ajé_Ìsẹ́gun_Ọjọ́rú_Ọjọ́bọ_Ẹtì_Àbámẹ́ta'.split('_'), + weekdaysShort : 'Àìk_Ajé_Ìsẹ́_Ọjr_Ọjb_Ẹtì_Àbá'.split('_'), + weekdaysMin : 'Àì_Aj_Ìs_Ọr_Ọb_Ẹt_Àb'.split('_'), + longDateFormat : { + LT : 'h:mm A', + LTS : 'h:mm:ss A', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY h:mm A', + LLLL : 'dddd, D MMMM YYYY h:mm A' + }, + calendar : { + sameDay : '[Ònì ni] LT', + nextDay : '[Ọ̀la ni] LT', + nextWeek : 'dddd [Ọsẹ̀ tón\'bọ] [ni] LT', + lastDay : '[Àna ni] LT', + lastWeek : 'dddd [Ọsẹ̀ tólọ́] [ni] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'ní %s', + past : '%s kọjá', + s : 'ìsẹjú aayá die', + m : 'ìsẹjú kan', + mm : 'ìsẹjú %d', + h : 'wákati kan', + hh : 'wákati %d', + d : 'ọjọ́ kan', + dd : 'ọjọ́ %d', + M : 'osù kan', + MM : 'osù %d', + y : 'ọdún kan', + yy : 'ọdún %d' + }, + dayOfMonthOrdinalParse : /ọjọ́\s\d{1,2}/, + ordinal : 'ọjọ́ %d', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); + +return yo; + +}))); diff --git a/lib/javascripts/moment_locale/zh-cn.js b/lib/javascripts/moment_locale/zh-cn.js old mode 100755 new mode 100644 index cbc3f87000..005e941f99 --- a/lib/javascripts/moment_locale/zh-cn.js +++ b/lib/javascripts/moment_locale/zh-cn.js @@ -1,92 +1,76 @@ //! moment.js locale configuration -//! locale : chinese (zh-cn) +//! locale : Chinese (China) [zh-cn] //! author : suupic : https://github.com/suupic //! author : Zeno Zeng : https://github.com/zenozeng ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var zh_cn = moment.defineLocale('zh-cn', { - months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), - monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), - weekdaysShort : '周日_周一_周二_周三_周四_周五_周六'.split('_'), - weekdaysMin : '日_一_二_三_四_五_六'.split('_'), - longDateFormat : { - LT : 'Ah点mm分', - LTS : 'Ah点m分s秒', - L : 'YYYY-MM-DD', - LL : 'YYYY年MMMD日', - LLL : 'YYYY年MMMD日Ah点mm分', - LLLL : 'YYYY年MMMD日ddddAh点mm分', - l : 'YYYY-MM-DD', - ll : 'YYYY年MMMD日', - lll : 'YYYY年MMMD日Ah点mm分', - llll : 'YYYY年MMMD日ddddAh点mm分' - }, - meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === '凌晨' || meridiem === '早上' || - meridiem === '上午') { - return hour; - } else if (meridiem === '下午' || meridiem === '晚上') { - return hour + 12; - } else { - // '中午' - return hour >= 11 ? hour : hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - var hm = hour * 100 + minute; - if (hm < 600) { - return '凌晨'; - } else if (hm < 900) { - return '早上'; - } else if (hm < 1130) { - return '上午'; - } else if (hm < 1230) { - return '中午'; - } else if (hm < 1800) { - return '下午'; - } else { - return '晚上'; - } - }, - calendar : { - sameDay : function () { - return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT'; - }, - nextDay : function () { - return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT'; - }, - lastDay : function () { - return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT'; - }, - nextWeek : function () { - var startOfWeek, prefix; - startOfWeek = moment().startOf('week'); - prefix = this.diff(startOfWeek, 'days') >= 7 ? '[下]' : '[本]'; - return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm'; - }, - lastWeek : function () { - var startOfWeek, prefix; - startOfWeek = moment().startOf('week'); - prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]'; - return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm'; - }, - sameElse : 'LL' - }, - ordinalParse: /\d{1,2}(日|月|周)/, - ordinal : function (number, period) { - switch (period) { +var zhCn = moment.defineLocale('zh-cn', { + months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), + monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), + weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), + weekdaysShort : '周日_周一_周二_周三_周四_周五_周六'.split('_'), + weekdaysMin : '日_一_二_三_四_五_六'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY年MMMD日', + LL : 'YYYY年MMMD日', + LLL : 'YYYY年MMMD日Ah点mm分', + LLLL : 'YYYY年MMMD日ddddAh点mm分', + l : 'YYYY年MMMD日', + ll : 'YYYY年MMMD日', + lll : 'YYYY年MMMD日 HH:mm', + llll : 'YYYY年MMMD日dddd HH:mm' + }, + meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, + meridiemHour: function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === '凌晨' || meridiem === '早上' || + meridiem === '上午') { + return hour; + } else if (meridiem === '下午' || meridiem === '晚上') { + return hour + 12; + } else { + // '中午' + return hour >= 11 ? hour : hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + var hm = hour * 100 + minute; + if (hm < 600) { + return '凌晨'; + } else if (hm < 900) { + return '早上'; + } else if (hm < 1130) { + return '上午'; + } else if (hm < 1230) { + return '中午'; + } else if (hm < 1800) { + return '下午'; + } else { + return '晚上'; + } + }, + calendar : { + sameDay : '[今天]LT', + nextDay : '[明天]LT', + nextWeek : '[下]ddddLT', + lastDay : '[昨天]LT', + lastWeek : '[上]ddddLT', + sameElse : 'L' + }, + dayOfMonthOrdinalParse: /\d{1,2}(日|月|周)/, + ordinal : function (number, period) { + switch (period) { case 'd': case 'D': case 'DDD': @@ -98,30 +82,30 @@ return number + '周'; default: return number; - } - }, - relativeTime : { - future : '%s内', - past : '%s前', - s : '几秒', - m : '1 分钟', - mm : '%d 分钟', - h : '1 小时', - hh : '%d 小时', - d : '1 天', - dd : '%d 天', - M : '1 个月', - MM : '%d 个月', - y : '1 年', - yy : '%d 年' - }, - week : { - // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. } - }); + }, + relativeTime : { + future : '%s内', + past : '%s前', + s : '几秒', + m : '1 分钟', + mm : '%d 分钟', + h : '1 小时', + hh : '%d 小时', + d : '1 天', + dd : '%d 天', + M : '1 个月', + MM : '%d 个月', + y : '1 年', + yy : '%d 年' + }, + week : { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } +}); - return zh_cn; +return zhCn; -})); \ No newline at end of file +}))); diff --git a/lib/javascripts/moment_locale/zh-hk.js b/lib/javascripts/moment_locale/zh-hk.js new file mode 100644 index 0000000000..5ded143d59 --- /dev/null +++ b/lib/javascripts/moment_locale/zh-hk.js @@ -0,0 +1,105 @@ +//! moment.js locale configuration +//! locale : Chinese (Hong Kong) [zh-hk] +//! author : Ben : https://github.com/ben-lin +//! author : Chris Lam : https://github.com/hehachris +//! author : Konstantin : https://github.com/skfd + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + +var zhHk = moment.defineLocale('zh-hk', { + months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), + monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), + weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), + weekdaysShort : '週日_週一_週二_週三_週四_週五_週六'.split('_'), + weekdaysMin : '日_一_二_三_四_五_六'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY年MMMD日', + LL : 'YYYY年MMMD日', + LLL : 'YYYY年MMMD日 HH:mm', + LLLL : 'YYYY年MMMD日dddd HH:mm', + l : 'YYYY年MMMD日', + ll : 'YYYY年MMMD日', + lll : 'YYYY年MMMD日 HH:mm', + llll : 'YYYY年MMMD日dddd HH:mm' + }, + meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') { + return hour; + } else if (meridiem === '中午') { + return hour >= 11 ? hour : hour + 12; + } else if (meridiem === '下午' || meridiem === '晚上') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + var hm = hour * 100 + minute; + if (hm < 600) { + return '凌晨'; + } else if (hm < 900) { + return '早上'; + } else if (hm < 1130) { + return '上午'; + } else if (hm < 1230) { + return '中午'; + } else if (hm < 1800) { + return '下午'; + } else { + return '晚上'; + } + }, + calendar : { + sameDay : '[今天]LT', + nextDay : '[明天]LT', + nextWeek : '[下]ddddLT', + lastDay : '[昨天]LT', + lastWeek : '[上]ddddLT', + sameElse : 'L' + }, + dayOfMonthOrdinalParse: /\d{1,2}(日|月|週)/, + ordinal : function (number, period) { + switch (period) { + case 'd' : + case 'D' : + case 'DDD' : + return number + '日'; + case 'M' : + return number + '月'; + case 'w' : + case 'W' : + return number + '週'; + default : + return number; + } + }, + relativeTime : { + future : '%s內', + past : '%s前', + s : '幾秒', + m : '1 分鐘', + mm : '%d 分鐘', + h : '1 小時', + hh : '%d 小時', + d : '1 天', + dd : '%d 天', + M : '1 個月', + MM : '%d 個月', + y : '1 年', + yy : '%d 年' + } +}); + +return zhHk; + +}))); diff --git a/lib/javascripts/moment_locale/zh-tw.js b/lib/javascripts/moment_locale/zh-tw.js old mode 100755 new mode 100644 index 664f5ec1f3..50a6b6a417 --- a/lib/javascripts/moment_locale/zh-tw.js +++ b/lib/javascripts/moment_locale/zh-tw.js @@ -1,71 +1,74 @@ //! moment.js locale configuration -//! locale : traditional chinese (zh-tw) +//! locale : Chinese (Taiwan) [zh-tw] //! author : Ben : https://github.com/ben-lin +//! author : Chris Lam : https://github.com/hehachris ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['moment'], factory) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : factory(global.moment) -}(this, function (moment) { 'use strict'; +}(this, (function (moment) { 'use strict'; - var zh_tw = moment.defineLocale('zh-tw', { - months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), - monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), - weekdaysShort : '週日_週一_週二_週三_週四_週五_週六'.split('_'), - weekdaysMin : '日_一_二_三_四_五_六'.split('_'), - longDateFormat : { - LT : 'Ah點mm分', - LTS : 'Ah點m分s秒', - L : 'YYYY年MMMD日', - LL : 'YYYY年MMMD日', - LLL : 'YYYY年MMMD日Ah點mm分', - LLLL : 'YYYY年MMMD日ddddAh點mm分', - l : 'YYYY年MMMD日', - ll : 'YYYY年MMMD日', - lll : 'YYYY年MMMD日Ah點mm分', - llll : 'YYYY年MMMD日ddddAh點mm分' - }, - meridiemParse: /早上|上午|中午|下午|晚上/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === '早上' || meridiem === '上午') { - return hour; - } else if (meridiem === '中午') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === '下午' || meridiem === '晚上') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - var hm = hour * 100 + minute; - if (hm < 900) { - return '早上'; - } else if (hm < 1130) { - return '上午'; - } else if (hm < 1230) { - return '中午'; - } else if (hm < 1800) { - return '下午'; - } else { - return '晚上'; - } - }, - calendar : { - sameDay : '[今天]LT', - nextDay : '[明天]LT', - nextWeek : '[下]ddddLT', - lastDay : '[昨天]LT', - lastWeek : '[上]ddddLT', - sameElse : 'L' - }, - ordinalParse: /\d{1,2}(日|月|週)/, - ordinal : function (number, period) { - switch (period) { +var zhTw = moment.defineLocale('zh-tw', { + months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), + monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), + weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), + weekdaysShort : '週日_週一_週二_週三_週四_週五_週六'.split('_'), + weekdaysMin : '日_一_二_三_四_五_六'.split('_'), + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'YYYY年MMMD日', + LL : 'YYYY年MMMD日', + LLL : 'YYYY年MMMD日 HH:mm', + LLLL : 'YYYY年MMMD日dddd HH:mm', + l : 'YYYY年MMMD日', + ll : 'YYYY年MMMD日', + lll : 'YYYY年MMMD日 HH:mm', + llll : 'YYYY年MMMD日dddd HH:mm' + }, + meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, + meridiemHour : function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') { + return hour; + } else if (meridiem === '中午') { + return hour >= 11 ? hour : hour + 12; + } else if (meridiem === '下午' || meridiem === '晚上') { + return hour + 12; + } + }, + meridiem : function (hour, minute, isLower) { + var hm = hour * 100 + minute; + if (hm < 600) { + return '凌晨'; + } else if (hm < 900) { + return '早上'; + } else if (hm < 1130) { + return '上午'; + } else if (hm < 1230) { + return '中午'; + } else if (hm < 1800) { + return '下午'; + } else { + return '晚上'; + } + }, + calendar : { + sameDay : '[今天]LT', + nextDay : '[明天]LT', + nextWeek : '[下]ddddLT', + lastDay : '[昨天]LT', + lastWeek : '[上]ddddLT', + sameElse : 'L' + }, + dayOfMonthOrdinalParse: /\d{1,2}(日|月|週)/, + ordinal : function (number, period) { + switch (period) { case 'd' : case 'D' : case 'DDD' : @@ -77,25 +80,25 @@ return number + '週'; default : return number; - } - }, - relativeTime : { - future : '%s內', - past : '%s前', - s : '幾秒', - m : '1分鐘', - mm : '%d分鐘', - h : '1小時', - hh : '%d小時', - d : '1天', - dd : '%d天', - M : '1個月', - MM : '%d個月', - y : '1年', - yy : '%d年' } - }); + }, + relativeTime : { + future : '%s內', + past : '%s前', + s : '幾秒', + m : '1 分鐘', + mm : '%d 分鐘', + h : '1 小時', + hh : '%d 小時', + d : '1 天', + dd : '%d 天', + M : '1 個月', + MM : '%d 個月', + y : '1 年', + yy : '%d 年' + } +}); - return zh_tw; +return zhTw; -})); \ No newline at end of file +}))); diff --git a/lib/middleware/turbo_dev.rb b/lib/middleware/turbo_dev.rb index d3b7eb3e1b..beda165224 100644 --- a/lib/middleware/turbo_dev.rb +++ b/lib/middleware/turbo_dev.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true module Middleware # Cheat and bypass Rails in development mode if the client attempts to download a static asset diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index e6b8c04756..9fbeb8ef83 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -238,6 +238,10 @@ class Plugin::Instance DiscoursePluginRegistry.custom_html.merge!(hash) end + def register_html_builder(name, &block) + DiscoursePluginRegistry.register_html_builder(name, &block) + end + def register_asset(file, opts=nil) full_path = File.dirname(path) << "/assets/" << file assets << [full_path, opts] diff --git a/lib/post_creator.rb b/lib/post_creator.rb index 864ea578a7..9da222b5cd 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -494,6 +494,8 @@ class PostCreator TopicUser.auto_notification_for_staging(@user.id, @topic.id, TopicUser.notification_reasons[:auto_watch]) elsif @user.user_option.notification_level_when_replying === NotificationLevels.topic_levels[:watching] TopicUser.auto_notification(@user.id, @topic.id, TopicUser.notification_reasons[:created_post], NotificationLevels.topic_levels[:watching]) + elsif @user.user_option.notification_level_when_replying === NotificationLevels.topic_levels[:regular] + TopicUser.auto_notification(@user.id, @topic.id, TopicUser.notification_reasons[:created_post], NotificationLevels.topic_levels[:regular]) else TopicUser.auto_notification(@user.id, @topic.id, TopicUser.notification_reasons[:created_post], NotificationLevels.topic_levels[:tracking]) end diff --git a/lib/post_destroyer.rb b/lib/post_destroyer.rb index 49fd91b887..22821fa5ea 100644 --- a/lib/post_destroyer.rb +++ b/lib/post_destroyer.rb @@ -13,6 +13,8 @@ class PostDestroyer end def self.destroy_stubs + context = I18n.t('remove_posts_deleted_by_author') + # exclude deleted topics and posts that are actively flagged Post.where(deleted_at: nil, user_deleted: true) .where("NOT EXISTS ( @@ -28,8 +30,9 @@ class PostDestroyer pa.deleted_at IS NULL AND pa.post_action_type_id IN (?) )", PostActionType.notify_flag_type_ids) - .each do |post| - PostDestroyer.new(Discourse.system_user, post, {context: I18n.t('remove_posts_deleted_by_author')}).destroy + .find_each do |post| + + PostDestroyer.new(Discourse.system_user, post, context: context).destroy end end diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index 036a50d6bd..d9000e102f 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -94,6 +94,10 @@ module PrettyText end end + DiscoursePluginRegistry.vendored_pretty_text.each do |vpt| + ctx.eval(File.read(vpt)) + end + ctx end diff --git a/lib/pretty_text/helpers.rb b/lib/pretty_text/helpers.rb index 1c0c2f0e1a..abcb5b6665 100644 --- a/lib/pretty_text/helpers.rb +++ b/lib/pretty_text/helpers.rb @@ -31,9 +31,8 @@ module PrettyText def mention_lookup(name) return false if name.blank? - return "group" if Group.where(name: name).exists? - return "user" if User.where(username_lower: name.downcase).exists? - nil + return "group" if Group.exists?(name: name) + return "user" if User.exists?(username_lower: name.downcase) end def category_hashtag_lookup(category_slug) @@ -45,7 +44,7 @@ module PrettyText end def get_topic_info(topic_id) - return unless Fixnum === topic_id + return unless topic_id.is_a?(Integer) # TODO this only handles public topics, secured one do not get this topic = Topic.find_by(id: topic_id) if topic && Guardian.new.can_see?(topic) @@ -70,10 +69,8 @@ module PrettyText end def get_current_user(user_id) - user = User.find_by(id: user_id) - staff = user ? user.staff? : false - { staff: staff } + return unless user_id.is_a?(Integer) + { staff: User.where(id: user_id).where("moderator OR admin").exists? } end end end - diff --git a/lib/sass/discourse_safe_sass_importer.rb b/lib/sass/discourse_safe_sass_importer.rb deleted file mode 100644 index 54ee6c5d24..0000000000 --- a/lib/sass/discourse_safe_sass_importer.rb +++ /dev/null @@ -1,32 +0,0 @@ -require_dependency 'sass/discourse_sass_importer' - -# This custom importer is used to import stylesheets but excludes plugins and theming. -# It's used as a fallback when compilation of stylesheets fails. - -class DiscourseSafeSassImporter < DiscourseSassImporter - def special_imports - super.merge({ - "plugins" => [], - "plugins_mobile" => [], - "plugins_desktop" => [], - "plugins_variables" => [] - }) - end - - def find(name, options) - if name == "theme_variables" - # Load the default variables - contents = "" - special_imports[name].each do |css_file| - contents << File.read(css_file) - end - ::Sass::Engine.new(contents, options.merge( - filename: "#{name}.scss", - importer: self, - syntax: :scss - )) - else - super(name, options) - end - end -end diff --git a/lib/sass/discourse_sass_compiler.rb b/lib/sass/discourse_sass_compiler.rb deleted file mode 100644 index a9c6c3e5b3..0000000000 --- a/lib/sass/discourse_sass_compiler.rb +++ /dev/null @@ -1,85 +0,0 @@ -require_dependency 'sass/discourse_sass_importer' -require 'pathname' - -module Sass::Script::Functions - def _error(message) - raise Sass::SyntaxError, mesage - end -end - -class DiscourseSassCompiler - - def self.compile(scss, target, opts={}) - self.new(scss, target).compile(opts) - end - - # Takes a Sass::SyntaxError and generates css that will show the - # error at the bottom of the page. - def self.error_as_css(sass_error, label) - error = sass_error.sass_backtrace_str(label) - error.gsub!("\n", '\A ') - error.gsub!("'", '\27 ') - - "footer { white-space: pre; } - footer:after { content: '#{error}' }" - end - - - def initialize(scss, target) - @scss = scss - @target = target - - unless Sass::Script::Functions < Sprockets::SassFunctions - Sass::Script::Functions.send :include, Sprockets::SassFunctions - end - end - - # Compiles the given scss and output the css as a string. - # - # Options: - # safe: (boolean) if true, theme and plugin stylesheets will not be included. Default is false. - def compile(opts={}) - app = Rails.application - env = app.assets || Sprockets::Railtie.build_environment(app) - - pathname = Pathname.new("app/assets/stylesheets/#{@target}.scss") - - context = env.context_class.new( - environment: env, - filename: "#{@target}.scss", - pathname: pathname, - metadata: {} - ) - - debug_opts = Rails.env.production? ? {} : { - line_numbers: true, - # debug_info: true, # great with Firebug + FireSass, but not helpful elsewhere - style: :expanded - } - - importer_class = opts[:safe] ? DiscourseSafeSassImporter : DiscourseSassImporter - - css = ::Sass::Engine.new(@scss, { - syntax: :scss, - cache: false, - read_cache: false, - style: :compressed, - filesystem_importer: importer_class, - load_paths: context.environment.paths.map { |path| importer_class.new(path.to_s) }, - sprockets: { - context: context, - environment: context.environment - } - }.merge(debug_opts)).render - - css_output = css - if opts[:rtl] - begin - require 'r2' - css_output = R2.r2(css) if defined?(R2) - rescue; end - end - css_output - end - -end diff --git a/lib/sass/discourse_sass_importer.rb b/lib/sass/discourse_sass_importer.rb deleted file mode 100644 index 62230db937..0000000000 --- a/lib/sass/discourse_sass_importer.rb +++ /dev/null @@ -1,100 +0,0 @@ -# This custom importer is used for site customizations. This is similar to the -# Sprockets::SassImporter implementation provided in sass-rails since that is used -# during asset precompilation. -class DiscourseSassImporter < Sass::Importers::Filesystem - module Sass - def extensions - { - 'css' => :scss, - 'css.scss' => :scss, - 'css.sass' => :sass, - 'css.erb' => :scss, - 'scss.erb' => :scss, - 'sass.erb' => :sass, - 'css.scss.erb' => :scss, - 'css.sass.erb' => :sass - }.merge!(super) - end - - def special_imports - { - "plugins" => DiscoursePluginRegistry.stylesheets, - "plugins_mobile" => DiscoursePluginRegistry.mobile_stylesheets, - "plugins_desktop" => DiscoursePluginRegistry.desktop_stylesheets, - "plugins_variables" => DiscoursePluginRegistry.sass_variables, - "theme_variables" => [ColorScheme::BASE_COLORS_FILE], - "category_backgrounds" => Proc.new { |c| "body.category-#{c.full_slug} { background-image: url(#{apply_cdn(c.uploaded_background.url)}) }\n" } - } - end - - def find_relative(name, base, options) - engine_from_path(name, File.dirname(base), options) - end - - def apply_cdn(url) - "#{GlobalSetting.cdn_url}#{url}" - end - - def find(name, options) - - if special_imports.has_key? name - case name - when "theme_variables" - contents = "" - ColorScheme.base_colors.each do |n, base_hex| - hex_val = ColorScheme.hex_for_name(n) || base_hex - contents << "$#{n}: ##{hex_val} !default;\n" - end - when "category_backgrounds" - contents = "" - Category.where('uploaded_background_id IS NOT NULL').each do |c| - contents << special_imports[name].call(c) if c.uploaded_background - end - else - stylesheets = special_imports[name] - contents = "" - stylesheets.each do |css_file| - if css_file =~ /\.scss$/ - contents << "@import '#{css_file}';" - else - contents << File.read(css_file) - end - depend_on(css_file) - end - end - - ::Sass::Engine.new(contents, options.merge( - filename: "#{name}.scss", - importer: self, - syntax: :scss - )) - else - engine_from_path(name, root, options) - end - end - - private - - def depend_on(filename) - if @context - @context.depend_on(filename) - @context.depend_on(globbed_file_parent(filename)) - end - end - - def engine_from_path(name, dir, options) - full_filename, _ = ::Sass::Util.destructure(find_real_file(dir, name, options)) - return unless full_filename && File.readable?(full_filename) - - depend_on(full_filename) - ::Sass::Engine.for_file(full_filename, options) - end - end - - include Sass - include ::Sass::Rails::SassImporter::Globbing - - def self.special_imports - self.new('').special_imports - end -end diff --git a/lib/sass/discourse_stylesheets.rb b/lib/sass/discourse_stylesheets.rb deleted file mode 100644 index cce67137e1..0000000000 --- a/lib/sass/discourse_stylesheets.rb +++ /dev/null @@ -1,178 +0,0 @@ -require_dependency 'sass/discourse_sass_compiler' -require_dependency 'distributed_cache' - -class DiscourseStylesheets - - CACHE_PATH ||= 'tmp/stylesheet-cache' - MANIFEST_DIR ||= "#{Rails.root}/tmp/cache/assets/#{Rails.env}" - MANIFEST_FULL_PATH ||= "#{MANIFEST_DIR}/stylesheet-manifest" - - @lock = Mutex.new - - def self.cache - return {} if Rails.env.development? - @cache ||= DistributedCache.new("discourse_stylesheet") - end - - def self.stylesheet_link_tag(target = :desktop, media = 'all') - - tag = cache[target] - - return tag.dup.html_safe if tag - - @lock.synchronize do - builder = self.new(target) - builder.compile unless File.exists?(builder.stylesheet_fullpath) - builder.ensure_digestless_file - tag = %[] - - cache[target] = tag - - tag.dup.html_safe - end - end - - def self.compile(target = :desktop, opts={}) - @lock.synchronize do - FileUtils.rm(MANIFEST_FULL_PATH, force: true) if opts[:force] - builder = self.new(target) - builder.compile(opts) - builder.stylesheet_filename - end - end - - def self.last_file_updated - if Rails.env.production? - @last_file_updated ||= if File.exists?(MANIFEST_FULL_PATH) - File.readlines(MANIFEST_FULL_PATH, 'r')[0] - else - mtime = max_file_mtime - FileUtils.mkdir_p(MANIFEST_DIR) - File.open(MANIFEST_FULL_PATH, "w") { |f| f.print(mtime) } - mtime - end - else - max_file_mtime - end - end - - def self.max_file_mtime - globs = ["#{Rails.root}/app/assets/stylesheets/**/*.*css"] - - Discourse.plugins.map { |plugin| File.dirname(plugin.path) }.each do |path| - globs += [ - "#{path}/plugin.rb", - "#{path}/**/*.*css", - ] - end - - globs.map do |pattern| - Dir.glob(pattern).map { |x| File.mtime(x) }.max - end.compact.max.to_i - end - - def initialize(target = :desktop) - @target = target - end - - def compile(opts={}) - unless opts[:force] - if File.exists?(stylesheet_fullpath) - unless StylesheetCache.where(target: @target, digest: digest).exists? - begin - StylesheetCache.add(@target, digest, File.read(stylesheet_fullpath)) - rescue => e - Rails.logger.warn "Completely unexpected error adding contents of '#{stylesheet_fullpath}' to cache #{e}" - end - end - return true - end - end - - scss = File.read("#{Rails.root}/app/assets/stylesheets/#{@target}.scss") - rtl = @target.to_s =~ /_rtl$/ - css = begin - DiscourseSassCompiler.compile(scss, @target, rtl: rtl) - rescue Sass::SyntaxError => e - Rails.logger.error "Stylesheet failed to compile for '#{@target}'! Recompiling without plugins and theming." - Rails.logger.error e.sass_backtrace_str("#{@target} stylesheet") - DiscourseSassCompiler.compile(scss + DiscourseSassCompiler.error_as_css(e, "#{@target} stylesheet"), @target, safe: true) - end - FileUtils.mkdir_p(cache_fullpath) - File.open(stylesheet_fullpath, "w") do |f| - f.puts css - end - begin - StylesheetCache.add(@target, digest, css) - rescue => e - Rails.logger.warn "Completely unexpected error adding item to cache #{e}" - end - css - end - - def ensure_digestless_file - # file without digest is only for auto-reloading css in dev env - unless Rails.env.production? || (File.exist?(stylesheet_fullpath_no_digest) && File.mtime(stylesheet_fullpath) == File.mtime(stylesheet_fullpath_no_digest)) - FileUtils.cp(stylesheet_fullpath, stylesheet_fullpath_no_digest) - end - end - - def self.cache_fullpath - "#{Rails.root}/#{CACHE_PATH}" - end - - def cache_fullpath - self.class.cache_fullpath - end - - def stylesheet_fullpath - "#{cache_fullpath}/#{stylesheet_filename}" - end - def stylesheet_fullpath_no_digest - "#{cache_fullpath}/#{stylesheet_filename_no_digest}" - end - - def stylesheet_cdnpath - "#{GlobalSetting.cdn_url}#{stylesheet_relpath}?__ws=#{Discourse.current_hostname}" - end - - def root_path - "#{GlobalSetting.relative_url_root}/" - end - - # using uploads cause we already have all the routing in place - def stylesheet_relpath - "#{root_path}stylesheets/#{stylesheet_filename}" - end - - def stylesheet_relpath_no_digest - "#{root_path}stylesheets/#{stylesheet_filename_no_digest}" - end - - def stylesheet_filename - "#{@target}_#{digest}.css" - end - def stylesheet_filename_no_digest - "#{@target}.css" - end - - # digest encodes the things that trigger a recompile - def digest - @digest ||= begin - theme = (cs = ColorScheme.enabled) ? "#{cs.id}-#{cs.version}" : false - category_updated = Category.where("uploaded_background_id IS NOT NULL").last_updated_at - - if theme || category_updated > 0 - Digest::SHA1.hexdigest "#{RailsMultisite::ConnectionManagement.current_db}-#{theme}-#{DiscourseStylesheets.last_file_updated}-#{category_updated}" - else - digest_string = "defaults-#{DiscourseStylesheets.last_file_updated}" - - if cdn_url = GlobalSetting.cdn_url - digest_string = "#{digest_string}-#{cdn_url}" - end - - Digest::SHA1.hexdigest digest_string - end - end - end -end diff --git a/lib/scheduler/manager.rb b/lib/scheduler/manager.rb index 1846aa7669..ec8e249e9b 100644 --- a/lib/scheduler/manager.rb +++ b/lib/scheduler/manager.rb @@ -78,6 +78,8 @@ module Scheduler start = Time.now.to_f info = @mutex.synchronize { @manager.schedule_info(klass) } stat = nil + error = nil + begin info.prev_result = "RUNNING" @mutex.synchronize { info.write! } @@ -91,11 +93,12 @@ module Scheduler ) end klass.new.perform - rescue Jobs::HandledExceptionWrapper - # Discourse.handle_exception was already called, and we don't have any extra info to give - failed = true rescue => e - Discourse.handle_job_exception(e, {message: "Running a scheduled job", job: klass}) + if e.class != Jobs::HandledExceptionWrapper + Discourse.handle_job_exception(e, message: "Running a scheduled job", job: klass) + end + + error = "#{e.class}: #{e.message} #{e.backtrace.join("\n")}" failed = true end duration = ((Time.now.to_f - start) * 1000).to_i @@ -103,10 +106,11 @@ module Scheduler info.prev_result = failed ? "FAILED" : "OK" info.current_owner = nil if stat - stat.update_columns( + stat.update!( duration_ms: duration, live_slots_finish: GC.stat[:heap_live_slots], - success: !failed + success: !failed, + error: error ) end attempts(3) do diff --git a/lib/site_setting_extension.rb b/lib/site_setting_extension.rb index 70e576f08d..20dd2818af 100644 --- a/lib/site_setting_extension.rb +++ b/lib/site_setting_extension.rb @@ -23,7 +23,7 @@ module SiteSettingExtension def types @types ||= Enum.new(string: 1, time: 2, - fixnum: 3, + integer: 3, float: 4, bool: 5, null: 6, @@ -286,7 +286,7 @@ module SiteSettingExtension val = (val == "t" || val == "true") ? 't' : 'f' end - if type == types[:fixnum] && !val.is_a?(Fixnum) + if type == types[:integer] && !val.is_a?(Integer) val = val.to_i end @@ -295,7 +295,7 @@ module SiteSettingExtension end if type == types[:enum] - val = val.to_i if defaults[name.to_sym].is_a?(Fixnum) + val = val.to_i if defaults[name.to_sym].is_a?(Integer) if enum_class(name) raise Discourse::InvalidParameters.new(:value) unless enum_class(name).valid_value?(val) else @@ -340,9 +340,9 @@ module SiteSettingExtension valid = true type = get_data_type(name, defaults[name.to_sym]) - if type == types[:fixnum] - # validate fixnum - valid = false unless value.to_i.is_a?(Fixnum) + if type == types[:integer] + # validate integer + valid = false unless value.to_i.is_a?(Integer) end valid @@ -407,8 +407,8 @@ module SiteSettingExtension case val when String types[:string] - when Fixnum - types[:fixnum] + when Integer + types[:integer] when Float types[:float] when TrueClass, FalseClass @@ -422,14 +422,14 @@ module SiteSettingExtension case type when types[:float] value.to_f - when types[:fixnum] + when types[:integer] value.to_i when types[:bool] value == true || value == "t" || value == "true" when types[:null] nil when types[:enum] - defaults[name.to_sym].is_a?(Fixnum) ? value.to_i : value + defaults[name.to_sym].is_a?(Integer) ? value.to_i : value else return value if types[type] # Otherwise it's a type error @@ -441,7 +441,7 @@ module SiteSettingExtension @validator_mapping ||= { 'email' => EmailSettingValidator, 'username' => UsernameSettingValidator, - types[:fixnum] => IntegerSettingValidator, + types[:integer] => IntegerSettingValidator, types[:string] => StringSettingValidator, 'list' => StringSettingValidator, 'enum' => StringSettingValidator, diff --git a/lib/socket_server.rb b/lib/socket_server.rb new file mode 100644 index 0000000000..9a2e0df54b --- /dev/null +++ b/lib/socket_server.rb @@ -0,0 +1,80 @@ +require 'socket' + +class SocketServer + + def initialize(socket_path) + @socket_path = socket_path + @server = nil + end + + def start(&blk) + @server = UNIXServer.new(@socket_path) + @accept_thread = new_accept_thread + if blk + @blk = blk + end + end + + def stop + @server.close if @server + @server = nil + @blk = nil + end + + protected + + def new_accept_thread + server = @server + Thread.new do + done = false + while !done + done = !accept_connection(server) + end + end + end + + def accept_connection(server) + socket = nil + begin + socket = server.accept + rescue IOError + # socket was shut down or something catastrophic like that happened + return false + end + + start = Time.now + line = "" + + while Time.now - start < 10 + if IO.select([socket], nil, nil, 10) + begin + line << socket.read_nonblock(1000) + rescue IO::WaitReadable + sleep 0.001 + end + end + break if line.include?("\n") + end + + if line.include?("\n") + socket.write get_response(line.strip) + end + + true + rescue IOError => e + # nothing to do here, case its normal on shutdown + rescue => e + Rails.logger.warn("Failed to handle connection in stats socket #{e}") + ensure + socket&.close rescue nil + end + + def get_response(command) + if @blk + @blk.call(command) + else + raise "Must be implemented by child" + end + end + +end diff --git a/lib/source_url.rb b/lib/source_url.rb index c089d18eb6..41d4fe5dec 100644 --- a/lib/source_url.rb +++ b/lib/source_url.rb @@ -1,6 +1,15 @@ class SourceURL < Tilt::Template self.default_mime_type = 'application/javascript' + def self.call(input) + filename = input[:filename] + source = input[:data] + context = input[:environment].context_class.new(input) + + result = new(filename){source}.render(context) + context.metadata.merge(data: result) + end + def prepare end diff --git a/lib/stats_socket.rb b/lib/stats_socket.rb new file mode 100644 index 0000000000..2b81db678f --- /dev/null +++ b/lib/stats_socket.rb @@ -0,0 +1,23 @@ +require 'socket_server' + +class StatsSocket < SocketServer + + def initialize(socket_path) + super(socket_path) + end + + protected + + def get_response(command) + result = + case command + when "gc_stat" + GC.stat.to_json + else + "[\"UNKNOWN COMMAND\"]" + end + + result << "\n" + end + +end diff --git a/lib/stylesheet/common.rb b/lib/stylesheet/common.rb new file mode 100644 index 0000000000..e133e7cd2d --- /dev/null +++ b/lib/stylesheet/common.rb @@ -0,0 +1,5 @@ +require 'sassc' + +module Stylesheet + ASSET_ROOT = "#{Rails.root}/app/assets/stylesheets" unless defined? ASSET_ROOT +end diff --git a/lib/stylesheet/compiler.rb b/lib/stylesheet/compiler.rb new file mode 100644 index 0000000000..a38bbfecc2 --- /dev/null +++ b/lib/stylesheet/compiler.rb @@ -0,0 +1,60 @@ +require_dependency 'stylesheet/common' +require_dependency 'stylesheet/importer' +require_dependency 'stylesheet/functions' + +module Stylesheet + + class Compiler + + def self.error_as_css(error, label) + error = error.message + error.gsub!("\n", '\A ') + error.gsub!("'", '\27 ') + + "footer { white-space: pre; } + footer:after { content: '#{error}' }" + end + + def self.compile_asset(asset, options={}) + + if Importer.special_imports[asset.to_s] + filename = "theme.scss" + file = "@import \"theme_variables\"; @import \"#{asset}\";" + else + filename = "#{asset}.scss" + path = "#{ASSET_ROOT}/#{filename}" + file = File.read path + end + + compile(file,filename,options) + + end + + def self.compile(stylesheet, filename, options={}) + source_map_file = options[:source_map_file] || "#{filename.sub(".scss","")}.css.map"; + + engine = SassC::Engine.new(stylesheet, + importer: Importer, + filename: filename, + style: :compressed, + source_map_file: source_map_file, + source_map_contents: true, + theme_id: options[:theme_id], + theme_field: options[:theme_field], + load_paths: [ASSET_ROOT]) + + + result = engine.render + + if options[:rtl] + require 'r2' + [R2.r2(result), nil] + else + source_map = engine.source_map + source_map.force_encoding("UTF-8") + + [result, source_map] + end + end + end +end diff --git a/lib/stylesheet/functions.rb b/lib/stylesheet/functions.rb new file mode 100644 index 0000000000..cd21bb9a9c --- /dev/null +++ b/lib/stylesheet/functions.rb @@ -0,0 +1,9 @@ +module Stylesheet + module ScssFunctions + def asset_url(path) + SassC::Script::String.new("url('#{ActionController::Base.helpers.asset_path(path.value)}')") + end + end +end + +::SassC::Script::Functions.send :include, Stylesheet::ScssFunctions diff --git a/lib/stylesheet/importer.rb b/lib/stylesheet/importer.rb new file mode 100644 index 0000000000..fe4eebda74 --- /dev/null +++ b/lib/stylesheet/importer.rb @@ -0,0 +1,131 @@ +require_dependency 'stylesheet/common' + +module Stylesheet + class Importer < SassC::Importer + + @special_imports = {} + + def self.special_imports + @special_imports + end + + def self.register_import(name, &blk) + @special_imports[name] = blk + end + + register_import "theme_field" do + Import.new("theme_field.scss", source: @theme_field) + end + + register_import "plugins" do + import_files(DiscoursePluginRegistry.stylesheets) + end + + register_import "plugins_mobile" do + import_files(DiscoursePluginRegistry.mobile_stylesheets) + end + + register_import "plugins_desktop" do + import_files(DiscoursePluginRegistry.desktop_stylesheets) + end + + register_import "plugins_variables" do + import_files(DiscoursePluginRegistry.sass_variables) + end + + register_import "theme_variables" do + contents = "" + colors = (@theme_id && theme.color_scheme) ? theme.color_scheme.resolved_colors : ColorScheme.base_colors + colors.each do |n, hex| + contents << "$#{n}: ##{hex} !default;\n" + end + Import.new("theme_variable.scss", source: contents) + end + + register_import "category_backgrounds" do + contents = "" + Category.where('uploaded_background_id IS NOT NULL').each do |c| + contents << category_css(c) if c.uploaded_background + end + + Import.new("categoy_background.scss", source: contents) + end + + register_import "embedded_theme" do + next unless @theme_id + + theme_import(:common, :embedded_scss) + end + + register_import "mobile_theme" do + next unless @theme_id + + theme_import(:mobile, :scss) + end + + register_import "desktop_theme" do + next unless @theme_id + + theme_import(:desktop, :scss) + end + + def initialize(options) + @theme_id = options[:theme_id] + @theme_field = options[:theme_field] + end + + def import_files(files) + files.map do |file| + # we never want inline css imports, they are a mess + # this tricks libsass so it imports inline instead + if file =~ /\.css$/ + file = file[0..-5] + end + Import.new(file) + end + end + + def theme_import(target, attr) + fields = theme.list_baked_fields(target, attr) + + fields.map do |field| + value = field.value + if value.present? + filename = "#{field.theme.id}/#{field.target_name}-#{field.name}-#{field.theme.name.parameterize}.scss" + with_comment = <] + end + + cache[cache_key] = tag + tag.dup.html_safe + end + end + + def self.precompile_css + themes = Theme.where('user_selectable OR key = ?', SiteSetting.default_theme_key).pluck(:key,:name) + themes << nil + themes.each do |key,name| + [:desktop, :mobile, :desktop_rtl, :mobile_rtl].each do |target| + theme_key = key || SiteSetting.default_theme_key + cache_key = "#{target}_#{theme_key}" + + STDERR.puts "precompile target: #{target} #{name}" + builder = self.new(target, theme_key) + builder.compile(force: true) + cache[cache_key] = nil + end + end + nil + end + + def self.last_file_updated + if Rails.env.production? + @last_file_updated ||= if File.exists?(MANIFEST_FULL_PATH) + File.readlines(MANIFEST_FULL_PATH, 'r')[0] + else + mtime = max_file_mtime + FileUtils.mkdir_p(MANIFEST_DIR) + File.open(MANIFEST_FULL_PATH, "w") { |f| f.print(mtime) } + mtime + end + else + max_file_mtime + end + end + + def self.max_file_mtime + globs = ["#{Rails.root}/app/assets/stylesheets/**/*.*css", + "#{Rails.root}/app/assets/images/**/*.*"] + + Discourse.plugins.map { |plugin| File.dirname(plugin.path) }.each do |path| + globs << "#{path}/plugin.rb" + globs << "#{path}/**/*.*css" + end + + globs.map do |pattern| + Dir.glob(pattern).map { |x| File.mtime(x) }.max + end.compact.max.to_i + end + + def initialize(target = :desktop, theme_key) + @target = target + @theme_key = theme_key + end + + def compile(opts={}) + unless opts[:force] + if File.exists?(stylesheet_fullpath) + unless StylesheetCache.where(target: qualified_target, digest: digest).exists? + begin + source_map = File.read(source_map_fullpath) rescue nil + StylesheetCache.add(qualified_target, digest, File.read(stylesheet_fullpath), source_map) + rescue => e + Rails.logger.warn "Completely unexpected error adding contents of '#{stylesheet_fullpath}' to cache #{e}" + end + end + return true + end + end + + rtl = @target.to_s =~ /_rtl$/ + css,source_map = begin + Stylesheet::Compiler.compile_asset( + @target, + rtl: rtl, + theme_id: theme&.id, + source_map_file: source_map_filename + ) + rescue SassC::SyntaxError => e + Rails.logger.error "Failed to compile #{@target} stylesheet: #{e.message}" + if %w{embedded_theme mobile_theme desktop_theme}.include?(@target.to_s) + # no special errors for theme, handled in theme editor + ["", nil] + else + [Stylesheet::Compiler.error_as_css(e, "#{@target} stylesheet"), nil] + end + end + + FileUtils.mkdir_p(cache_fullpath) + + File.open(stylesheet_fullpath, "w") do |f| + f.puts css + end + + if source_map.present? + File.open(source_map_fullpath, "w") do |f| + f.puts source_map + end + end + + begin + StylesheetCache.add(qualified_target, digest, css, source_map) + rescue => e + Rails.logger.warn "Completely unexpected error adding item to cache #{e}" + end + css + end + + def self.cache_fullpath + "#{Rails.root}/#{CACHE_PATH}" + end + + def cache_fullpath + self.class.cache_fullpath + end + + def stylesheet_fullpath + "#{cache_fullpath}/#{stylesheet_filename}" + end + + def source_map_fullpath + "#{cache_fullpath}/#{source_map_filename}" + end + + def source_map_filename + "#{stylesheet_filename}.map" + end + + def stylesheet_fullpath_no_digest + "#{cache_fullpath}/#{stylesheet_filename_no_digest}" + end + + def stylesheet_cdnpath + "#{GlobalSetting.cdn_url}#{stylesheet_relpath}?__ws=#{Discourse.current_hostname}" + end + + def stylesheet_path + stylesheet_cdnpath + end + + def root_path + "#{GlobalSetting.relative_url_root}/" + end + + def stylesheet_relpath + "#{root_path}stylesheets/#{stylesheet_filename}" + end + + def stylesheet_relpath_no_digest + "#{root_path}stylesheets/#{stylesheet_filename_no_digest}" + end + + def qualified_target + if is_theme? + "#{@target}_#{theme.id}" + else + scheme_string = theme && theme.color_scheme ? "_#{theme.color_scheme.id}" : "" + "#{@target}#{scheme_string}" + end + end + + def stylesheet_filename(with_digest = true) + digest_string = "_#{self.digest}" if with_digest + "#{qualified_target}#{digest_string}.css" + end + + def stylesheet_filename_no_digest + stylesheet_filename(_with_digest=false) + end + + def is_theme? + !!(@target.to_s =~ /_theme$/) + end + + # digest encodes the things that trigger a recompile + def digest + @digest ||= begin + if is_theme? + theme_digest + else + color_scheme_digest + end + end + end + + def theme + @theme ||= (Theme.find_by(key: @theme_key) || :nil) + @theme == :nil ? nil : @theme + end + + def theme_digest + scss = "" + + if [:mobile_theme, :desktop_theme].include?(@target) + scss = theme.resolve_baked_field(:common, :scss) + scss += theme.resolve_baked_field(@target.to_s.sub("_theme", ""), :scss) + elsif @target == :embedded_theme + scss = theme.resolve_baked_field(:common, :embedded_scss) + else + raise "attempting to look up theme digest for invalid field" + end + + Digest::SHA1.hexdigest scss.to_s + end + + def color_scheme_digest + + cs = theme&.color_scheme + category_updated = Category.where("uploaded_background_id IS NOT NULL").last_updated_at + + if cs || category_updated > 0 + Digest::SHA1.hexdigest "#{RailsMultisite::ConnectionManagement.current_db}-#{cs&.id}-#{cs&.version}-#{Stylesheet::Manager.last_file_updated}-#{category_updated}" + else + digest_string = "defaults-#{Stylesheet::Manager.last_file_updated}" + + if cdn_url = GlobalSetting.cdn_url + digest_string = "#{digest_string}-#{cdn_url}" + end + + Digest::SHA1.hexdigest digest_string + end + end +end diff --git a/lib/stylesheet/watcher.rb b/lib/stylesheet/watcher.rb new file mode 100644 index 0000000000..138d750fd6 --- /dev/null +++ b/lib/stylesheet/watcher.rb @@ -0,0 +1,73 @@ +require 'listen' + +module Stylesheet + class Watcher + + def self.watch(paths=nil) + watcher = new(paths) + watcher.start + watcher + end + + def initialize(paths) + @paths = paths || ["app/assets/stylesheets", "plugins"] + @queue = Queue.new + end + + def start + + Thread.new do + begin + while true + worker_loop + end + rescue => e + STDERR.puts "CSS change notifier crashed #{e}" + end + end + + + root = Rails.root.to_s + @paths.each do |watch| + Thread.new do + begin + listener = Listen.to("#{root}/#{watch}", ignore: /xxxx/) do |modified, added, _| + paths = [modified, added].flatten + paths.compact! + paths.map!{|long| long[(root.length+1)..-1]} + process_change(paths) + end + rescue => e + STDERR.puts "Failed to listen for CSS changes at: #{watch}\n#{e}" + end + listener.start + sleep + end + end + end + + def worker_loop + @queue.pop + while @queue.length > 0 + @queue.pop + end + + Stylesheet::Manager.cache.clear + + message = ["desktop", "mobile", "admin"].map do |name| + {target: name, new_href: Stylesheet::Manager.stylesheet_href(name.to_sym) , theme_key: SiteSetting.default_theme_key} + end + + MessageBus.publish '/file-change', message + end + + def process_change(paths) + paths.each do |path| + if path =~ /\.(css|scss)$/ + @queue.push path + end + end + end + + end +end diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake index 23e036f57c..28f6d35cd8 100644 --- a/lib/tasks/assets.rake +++ b/lib/tasks/assets.rake @@ -49,11 +49,9 @@ task 'assets:precompile:css' => 'environment' do # Heroku precompiles assets before db migration, so tables may not exist. # css will get precompiled during first request instead in that case. - if ActiveRecord::Base.connection.table_exists?(ColorScheme.table_name) - STDERR.puts "Compiling css for #{db}" - [:desktop, :mobile, :desktop_rtl, :mobile_rtl].each do |target| - STDERR.puts "target: #{target} #{DiscourseStylesheets.compile(target)}" - end + if ActiveRecord::Base.connection.table_exists?(Theme.table_name) + STDERR.puts "Compiling css for #{db} #{Time.zone.now}" + Stylesheet::Manager.precompile_css end end @@ -134,8 +132,6 @@ def concurrent? end task 'assets:precompile' => 'assets:precompile:before' do - # Run after assets:precompile - Rake::Task["assets:precompile:css"].invoke if $bypass_sprockets_uglify puts "Compressing Javascript and Generating Source Maps" @@ -184,8 +180,21 @@ task 'assets:precompile' => 'assets:precompile:before' do STDERR.puts e.backtrace end end - - end end + +Rake::Task["assets:precompile"].enhance do + class Sprockets::Manifest + def reload + @filename = find_directory_manifest(@directory) + @data = json_decode(File.read(@filename)) + end + end + + # cause on boot we loaded a blank manifest, + # we need to know where all the assets are to precompile CSS + # cause CSS uses asset_path + Rails.application.assets_manifest.reload + Rake::Task["assets:precompile:css"].invoke +end diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake new file mode 100644 index 0000000000..a462631c8c --- /dev/null +++ b/lib/tasks/import.rake @@ -0,0 +1,431 @@ +# Use http://tatiyants.com/pev/#/plans/new if you want to optimize a query + +task "import:ensure_consistency" => :environment do + log "Starting..." + + insert_post_timings + insert_post_replies + insert_topic_users + insert_topic_views + insert_user_actions + insert_user_options + insert_user_stats + insert_user_visits + insert_draft_sequences + + update_user_stats + update_posts + update_topics + update_categories + update_users + update_groups + + log "Done!" +end + +MS_SPEND_CREATING_POST ||= 5000 + +def insert_post_timings + log "Inserting post timings..." + + exec_sql <<-SQL + INSERT INTO post_timings (topic_id, post_number, user_id, msecs) + SELECT topic_id, post_number, user_id, #{MS_SPEND_CREATING_POST} + FROM posts + WHERE user_id > 0 + ON CONFLICT DO NOTHING + SQL +end + +def insert_post_replies + log "Inserting post replies..." + + exec_sql <<-SQL + INSERT INTO post_replies (post_id, reply_id, created_at, updated_at) + SELECT p2.id, p.id, p.created_at, p.created_at + FROM posts p + INNER JOIN posts p2 ON p2.post_number = p.reply_to_post_number AND p2.topic_id = p.topic_id + ON CONFLICT DO NOTHING + SQL +end + +def insert_topic_users + log "Inserting topic users..." + + exec_sql <<-SQL + INSERT INTO topic_users (user_id, topic_id, posted, last_read_post_number, highest_seen_post_number, first_visited_at, last_visited_at, total_msecs_viewed) + SELECT user_id, topic_id, 't' , MAX(post_number), MAX(post_number), MIN(created_at), MAX(created_at), COUNT(id) * #{MS_SPEND_CREATING_POST} + FROM posts + WHERE user_id > 0 + GROUP BY user_id, topic_id + ON CONFLICT DO NOTHING + SQL +end + +def insert_topic_views + log "Inserting topic views..." + + exec_sql <<-SQL + WITH X AS ( + SELECT topic_id, user_id, DATE(p.created_at) posted_at + FROM posts p + JOIN users u ON u.id = p.user_id + WHERE user_id > 0 + GROUP BY topic_id, user_id, DATE(p.created_at) + ) + INSERT INTO topic_views (topic_id, user_id, viewed_at, ip_address) + SELECT X.topic_id, X.user_id, X.posted_at, ip_address + FROM X + JOIN users u ON u.id = X.user_id + WHERE ip_address IS NOT NULL + ON CONFLICT DO NOTHING + SQL +end + +def insert_user_actions + log "Inserting user actions for NEW_TOPIC = 4..." + + exec_sql <<-SQL + INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) + SELECT 4, p.user_id, topic_id, p.id, p.user_id, p.created_at, p.created_at + FROM posts p + JOIN topics t ON t.id = p.topic_id + WHERE post_number = 1 + AND archetype <> 'private_message' + AND p.deleted_at IS NULL + AND t.deleted_at IS NULL + ON CONFLICT DO NOTHING + SQL + + log "Inserting user actions for REPLY = 5..." + + exec_sql <<-SQL + INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) + SELECT 5, p.user_id, topic_id, p.id, p.user_id, p.created_at, p.created_at + FROM posts p + JOIN topics t ON t.id = p.topic_id + WHERE post_number > 1 + AND archetype <> 'private_message' + AND p.deleted_at IS NULL + AND t.deleted_at IS NULL + ON CONFLICT DO NOTHING + SQL + + log "Inserting user actions for RESPONSE = 6..." + + exec_sql <<-SQL + INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) + SELECT 6, p.user_id, p.topic_id, p.id, p2.user_id, p.created_at, p.created_at + FROM posts p + JOIN topics t ON t.id = p.topic_id + INNER JOIN posts p2 ON p2.post_number = p.reply_to_post_number + WHERE p.post_number > 1 + AND archetype <> 'private_message' + AND p.deleted_at IS NULL + AND t.deleted_at IS NULL + AND p2.topic_id = p.topic_id + AND p2.user_id <> p.user_id + ON CONFLICT DO NOTHING + SQL + + # TODO: + # - NEW_PRIVATE_MESSAGE + # - GOT_PRIVATE_MESSAGE +end + +def insert_user_options + log "Inserting user options..." + + exec_sql <<-SQL + INSERT INTO user_options ( + user_id, + email_always, + mailing_list_mode, + mailing_list_mode_frequency, + email_direct, + email_private_messages, + email_previous_replies, + email_in_reply_to, + email_digests, + digest_after_minutes, + include_tl0_in_digests, + automatically_unpin_topics, + enable_quoting, + external_links_in_new_tab, + dynamic_favicon, + disable_jump_reply, + new_topic_duration_minutes, + auto_track_topics_after_msecs, + notification_level_when_replying, + like_notification_frequency + ) + SELECT u.id + , #{SiteSetting.default_email_always} + , #{SiteSetting.default_email_mailing_list_mode} + , #{SiteSetting.default_email_mailing_list_mode_frequency} + , #{SiteSetting.default_email_direct} + , #{SiteSetting.default_email_private_messages} + , #{SiteSetting.default_email_previous_replies} + , #{SiteSetting.default_email_in_reply_to} + , #{SiteSetting.default_email_digest_frequency.to_i > 0} + , #{SiteSetting.default_email_digest_frequency} + , #{SiteSetting.default_include_tl0_in_digests} + , #{SiteSetting.default_topics_automatic_unpin} + , #{SiteSetting.default_other_enable_quoting} + , #{SiteSetting.default_other_external_links_in_new_tab} + , #{SiteSetting.default_other_dynamic_favicon} + , #{SiteSetting.default_other_disable_jump_reply} + , #{SiteSetting.default_other_new_topic_duration_minutes} + , #{SiteSetting.default_other_auto_track_topics_after_msecs} + , #{SiteSetting.default_other_notification_level_when_replying} + , #{SiteSetting.default_other_like_notification_frequency} + FROM users u + LEFT JOIN user_options uo ON uo.user_id = u.id + WHERE uo.user_id IS NULL + SQL +end + +def insert_user_stats + log "Inserting user stats..." + + exec_sql <<-SQL + INSERT INTO user_stats (user_id, new_since) + SELECT id, created_at + FROM users + ON CONFLICT DO NOTHING + SQL +end + +def insert_user_visits + log "Inserting user visits..." + + exec_sql <<-SQL + INSERT INTO user_visits (user_id, visited_at, posts_read) + SELECT user_id, DATE(created_at), COUNT(*) + FROM posts + WHERE user_id > 0 + GROUP BY user_id, DATE(created_at) + ON CONFLICT DO NOTHING + SQL +end + +def insert_draft_sequences + log "Inserting draft sequences..." + + exec_sql <<-SQL + INSERT INTO draft_sequences (user_id, draft_key, sequence) + SELECT user_id, CONCAT('#{Draft::EXISTING_TOPIC}', id), 1 + FROM topics + WHERE user_id > 0 + AND archetype = 'regular' + ON CONFLICT DO NOTHING + SQL +end + +def update_user_stats + log "Updating user stats..." + + exec_sql <<-SQL + WITH X AS ( + SELECT p.user_id + , COUNT(p.id) posts + , COUNT(DISTINCT p.topic_id) topics + , MIN(p.created_at) min_created_at + , COALESCE(COUNT(DISTINCT DATE(p.created_at)), 0) days + , COALESCE(( + SELECT COUNT(*) + FROM topics + WHERE id IN ( + SELECT topic_id + FROM posts p2 + JOIN topics t2 ON t2.id = p2.topic_id + WHERE p2.deleted_at IS NULL + AND p2.post_type = 1 + AND NOT COALESCE(p2.hidden, 't') + AND p2.user_id <> t2.user_id + AND p2.user_id = p.user_id + ) + ), 0) topic_replies + FROM posts p + JOIN topics t ON t.id = p.topic_id + WHERE p.deleted_at IS NULL + AND NOT COALESCE(p.hidden, 't') + AND p.post_type = 1 + AND t.deleted_at IS NULL + AND COALESCE(t.visible, 't') + AND t.archetype <> 'private_message' + AND p.user_id > 0 + GROUP BY p.user_id + ) + UPDATE user_stats + SET post_count = X.posts + , posts_read_count = X.posts + , time_read = X.posts * 5 + , topic_count = X.topics + , topics_entered = X.topics + , first_post_created_at = X.min_created_at + , days_visited = X.days + , topic_reply_count = X.topic_replies + FROM X + WHERE user_stats.user_id = X.user_id + AND (post_count <> X.posts + OR posts_read_count <> X.posts + OR time_read <> X.posts * 5 + OR topic_count <> X.topics + OR topics_entered <> X.topics + OR COALESCE(first_post_created_at, '1970-01-01') <> X.min_created_at + OR days_visited <> X.days + OR topic_reply_count <> X.topic_replies) + SQL +end + +def update_posts + log "Updating posts..." + + exec_sql <<-SQL + WITH Y AS ( + SELECT post_id, COUNT(*) replies FROM post_replies GROUP BY post_id + ) + UPDATE posts + SET reply_count = Y.replies + FROM Y + WHERE posts.id = Y.post_id + AND reply_count <> Y.replies + SQL + + + # -- TODO: ensure this is how this works! + # WITH X AS ( + # SELECT pr.post_id, p.user_id + # FROM post_replies pr + # JOIN posts p ON p.id = pr.reply_id + # ) + # UPDATE posts + # SET reply_to_user_id = X.user_id + # FROM X + # WHERE id = X.post_id + # AND COALESCE(reply_to_user_id, -9999) <> X.user_id +end + +def update_topics + log "Updating topics..." + + exec_sql <<-SQL + WITH X AS ( + SELECT topic_id + , COUNT(*) posts + , MAX(created_at) last_post_date + , COALESCE(SUM(word_count), 0) words + , COALESCE(SUM(reply_count), 0) replies + , ( SELECT user_id + FROM posts + WHERE NOT hidden + AND deleted_at IS NULL + AND topic_id = p.topic_id + ORDER BY post_number DESC + LIMIT 1) last_poster + FROM posts p + WHERE NOT hidden + AND deleted_at IS NULL + GROUP BY topic_id + ) + UPDATE topics + SET posts_count = X.posts + , last_posted_at = X.last_post_date + , bumped_at = X.last_post_date + , word_count = X.words + , reply_count = X.replies + , last_post_user_id = X.last_poster + FROM X + WHERE id = X.topic_id + AND (posts_count <> X.posts + OR COALESCE(last_posted_at, '1970-01-01') <> X.last_post_date + OR bumped_at <> X.last_post_date + OR COALESCE(word_count, -1) <> X.words + OR COALESCE(reply_count, -1) <> X.replies + OR COALESCE(last_post_user_id, -9999) <> X.last_poster) + SQL +end + +def update_categories + log "Updating categories..." + + exec_sql <<-SQL + WITH X AS ( + SELECT category_id + , MAX(p.id) post_id + , MAX(t.id) topic_id + , COUNT(p.id) posts + , COUNT(DISTINCT p.topic_id) topics + FROM posts p + JOIN topics t ON t.id = p.topic_id + WHERE p.deleted_at IS NULL + AND t.deleted_at IS NULL + AND NOT p.hidden + AND t.visible + GROUP BY category_id + ) + UPDATE categories + SET latest_post_id = X.post_id + , latest_topic_id = X.topic_id + , post_count = X.posts + , topic_count = X.topics + FROM X + WHERE id = X.category_id + AND (COALESCE(latest_post_id, -1) <> X.post_id + OR COALESCE(latest_topic_id, -1) <> X.topic_id + OR post_count <> X.posts + OR topic_count <> X.topics) + SQL +end + +def update_users + log "Updating users..." + + exec_sql <<-SQL + WITH X AS ( + SELECT user_id + , MIN(created_at) min_created_at + , MAX(created_at) max_created_at + FROM posts + WHERE deleted_at IS NULL + GROUP BY user_id + ) + UPDATE users + SET first_seen_at = X.min_created_at + , last_seen_at = X.max_created_at + , last_posted_at = X.max_created_at + FROM X + WHERE id = X.user_id + AND (COALESCE(first_seen_at, '1970-01-01') <> X.min_created_at + OR COALESCE(last_seen_at, '1970-01-01') <> X.max_created_at + OR COALESCE(last_posted_at, '1970-01-01') <> X.max_created_at) + SQL +end + +def update_groups + log "Updating groups..." + + exec_sql <<-SQL + WITH X AS ( + SELECT group_id, COUNT(*) count + FROM group_users + GROUP BY group_id + ) + UPDATE groups + SET user_count = X.count + FROM X + WHERE id = X.group_id + AND user_count <> X.count + SQL +end + +def log(message) + puts "[#{DateTime.now.strftime("%Y-%m-%d %H:%M:%S")}] #{message}" +end + +def exec_sql(sql) + ActiveRecord::Base.transaction do + ActiveRecord::Base.exec_sql(sql) + end +end diff --git a/lib/topic_creator.rb b/lib/topic_creator.rb index 8c0621d507..ecd35fd116 100644 --- a/lib/topic_creator.rb +++ b/lib/topic_creator.rb @@ -64,7 +64,7 @@ class TopicCreator rollback_with!(topic, :too_many_users) if @added_users.size != 1 # Create a warning record - Warning.create(topic: topic, user: @added_users.first, created_by: @user) + UserWarning.create(topic: topic, user: @added_users.first, created_by: @user) end def watch_topic(topic) diff --git a/lib/validators/censored_words_validator.rb b/lib/validators/censored_words_validator.rb index ed97a13d2a..3b566a27ea 100644 --- a/lib/validators/censored_words_validator.rb +++ b/lib/validators/censored_words_validator.rb @@ -1,15 +1,11 @@ class CensoredWordsValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - if !SiteSetting.censored_words.blank? && - !(censored_words = value.scan(censored_words_regexp)).empty? - + if SiteSetting.censored_words.present? && (censored_words = censor_words(value, censored_words_regexp)).present? record.errors.add( attribute, :contains_censored_words, censored_words: join_censored_words(censored_words) ) - elsif !SiteSetting.censored_pattern.blank? && - !(censored_words = value.scan(/#{SiteSetting.censored_pattern}/i)).empty? - + elsif SiteSetting.censored_pattern.present? && (censored_words = censor_words(value, /#{SiteSetting.censored_pattern}/i)).present? record.errors.add( attribute, :matches_censored_pattern, censored_words: join_censored_words(censored_words) @@ -19,6 +15,16 @@ class CensoredWordsValidator < ActiveModel::EachValidator private + def censor_words(value, regexp) + censored_words = value.scan(regexp) + censored_words.flatten! + censored_words.compact! + censored_words.map!(&:strip) + censored_words.select!(&:present?) + censored_words.uniq! + censored_words + end + def join_censored_words(censored_words) censored_words.map!(&:downcase) censored_words.uniq! diff --git a/lib/validators/integer_setting_validator.rb b/lib/validators/integer_setting_validator.rb index 2c92f3c20b..185eb0a799 100644 --- a/lib/validators/integer_setting_validator.rb +++ b/lib/validators/integer_setting_validator.rb @@ -1,6 +1,8 @@ class IntegerSettingValidator def initialize(opts={}) @opts = opts + @opts[:min] = 0 unless @opts[:min].present? || @opts[:hidden] + @opts[:max] = 20000 unless @opts[:max].present? || @opts[:hidden] end def valid_value?(val) diff --git a/lib/validators/post_validator.rb b/lib/validators/post_validator.rb index a1836eb8f3..3ed62e3740 100644 --- a/lib/validators/post_validator.rb +++ b/lib/validators/post_validator.rb @@ -30,7 +30,7 @@ class Validators::PostValidator < ActiveModel::Validator end def post_body_validator(post) - return if options[:skip_post_body] + return if options[:skip_post_body] || post.topic&.pm_with_non_human_user? stripped_length(post) raw_quality(post) end diff --git a/lib/version.rb b/lib/version.rb index ab63b0e6f5..6ffa9a439d 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -5,7 +5,7 @@ module Discourse MAJOR = 1 MINOR = 8 TINY = 0 - PRE = 'beta10' + PRE = 'beta11' STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/lib/wizard/builder.rb b/lib/wizard/builder.rb index 17de742274..9600a9ab57 100644 --- a/lib/wizard/builder.rb +++ b/lib/wizard/builder.rb @@ -114,28 +114,31 @@ class Wizard end @wizard.append_step('colors') do |step| - theme_id = ColorScheme.where(via_wizard: true).pluck(:theme_id) - theme_id = theme_id.present? ? theme_id[0] : 'default' + scheme_id = ColorScheme.where(via_wizard: true).pluck(:base_scheme_id)&.first + scheme_id ||= 'default' - themes = step.add_field(id: 'theme_id', type: 'dropdown', required: true, value: theme_id) - ColorScheme.themes.each {|t| themes.add_choice(t[:id], data: t) } + themes = step.add_field(id: 'base_scheme_id', type: 'dropdown', required: true, value: scheme_id) + ColorScheme.base_color_scheme_colors.each do |t| + with_hash = t[:colors].dup + with_hash.map{|k,v| with_hash[k] = "##{v}"} + themes.add_choice(t[:id], data: {colors: with_hash}) + end step.add_field(id: 'theme_preview', type: 'component') step.on_update do |updater| - scheme_name = updater.fields[:theme_id] + scheme_name = updater.fields[:base_scheme_id] - theme = ColorScheme.themes.find {|s| s[:id] == scheme_name } + theme = ColorScheme.base_color_schemes.find{|s| s.base_scheme_id == scheme_name} colors = [] - theme[:colors].each do |name, hex| - colors << {name: name, hex: hex[1..-1] } + theme.colors.each do |color| + colors << {name: color.name, hex: color.hex } end attrs = { - enabled: true, name: I18n.t("wizard.step.colors.fields.theme_id.choices.#{scheme_name}.label"), colors: colors, - theme_id: scheme_name + base_scheme_id: scheme_name } scheme = ColorScheme.where(via_wizard: true).first @@ -148,6 +151,14 @@ class Wizard scheme = ColorScheme.new(attrs) scheme.save! end + + default_theme = Theme.find_by(key: SiteSetting.default_theme_key) + unless default_theme + default_theme = Theme.new(name: "Default Theme", user_id: -1) + end + default_theme.color_scheme_id = scheme.id + default_theme.save! + SiteSetting.default_theme_key = default_theme.key end end @@ -224,7 +235,11 @@ class Wizard users.each do |u| args = {} args[:moderator] = true if u['role'] == 'moderator' - Invite.create_invite_by_email(u['email'], @wizard.user, args) + begin + Invite.create_invite_by_email(u['email'], @wizard.user, args) + rescue => e + updater.errors.add(:invite_list, e.message.concat("
    ")) + end end end end @@ -250,4 +265,3 @@ class Wizard end end end - diff --git a/plugins/poll/config/locales/client.da.yml b/plugins/poll/config/locales/client.da.yml index 8cfdeaaf03..1576928ec8 100644 --- a/plugins/poll/config/locales/client.da.yml +++ b/plugins/poll/config/locales/client.da.yml @@ -67,4 +67,4 @@ da: poll_public: label: Vis hvem der stemte poll_options: - label: Indtast en afstemning pr. linje + label: Indtast en valgmulighed per linje diff --git a/plugins/poll/config/locales/client.es.yml b/plugins/poll/config/locales/client.es.yml index d31ecc9903..a2e2bc98c9 100644 --- a/plugins/poll/config/locales/client.es.yml +++ b/plugins/poll/config/locales/client.es.yml @@ -54,6 +54,7 @@ es: insert: Insertar Encuesta help: options_count: Introducir al menos 2 opciones + invalid_values: El valor mínimo debe ser menor que el valor máximo. poll_type: label: Tipo regular: Una opción diff --git a/plugins/poll/config/locales/client.fa_IR.yml b/plugins/poll/config/locales/client.fa_IR.yml index 064c381502..ef5b603897 100644 --- a/plugins/poll/config/locales/client.fa_IR.yml +++ b/plugins/poll/config/locales/client.fa_IR.yml @@ -15,6 +15,15 @@ fa_IR: average_rating: "میانگین امتیاز: %{average}." public: title: "آرا عمومی هستند" + multiple: + help: + at_least_min_options: + other: "حداقل %{count} گزینه انتخاب کنید." + up_to_max_options: + other: "حداکثر %{count} گزینه انتخاب کنید" + x_options: + other: "%{count} گزینه انتخاب کنید" + between_min_and_max_options: "بین %{min} تا %{max} گزینه انتخاب کنید" cast-votes: title: "انداختن رأی شما" label: "رای بدهید!" @@ -32,15 +41,25 @@ fa_IR: title: "نظرسنجی را ببند" label: "بسته " confirm: "آیا از بستن این نظرسنجی اطمینان دارید ؟ " + error_while_toggling_status: "متاسفانه در تغییر وضعیت این نظرسنجی، خطایی رخ داده است." + error_while_casting_votes: "متاسفانه در ثبت رای شما خطایی رخ داده است." + error_while_fetching_voters: "متاسفانه در نمایش رای دهندگان خطایی رخ داده است." ui_builder: title: ساخت نظرسنجی + insert: درج نظر سنجی + help: + options_count: حداقل 2 گزینه وارد کنید + invalid_values: مقدار حداقل می بایست کمتر از مقدار حداکثر باشد. poll_type: label: نوع regular: یک انتخاب - multiple: انتخاب چندگانه + multiple: چند انتخاب + number: امتیاز عددی poll_config: max: بیشترین min: کمترین step: مرحله poll_public: - label: نشان دادن رای دهندگان + label: نمایش رای دهندگان + poll_options: + label: در هر خط یک گزینه نظرسنجی را وارد کنید diff --git a/plugins/poll/config/locales/client.fr.yml b/plugins/poll/config/locales/client.fr.yml index 32dc6ec804..dfad51e304 100644 --- a/plugins/poll/config/locales/client.fr.yml +++ b/plugins/poll/config/locales/client.fr.yml @@ -54,6 +54,7 @@ fr: insert: Insérer le sondage help: options_count: Entrez au moins 2 options + invalid_values: La valeur minimum doit être plus petite que la valeur maximum. poll_type: label: Type regular: Choix unique diff --git a/plugins/poll/config/locales/client.it.yml b/plugins/poll/config/locales/client.it.yml index 26c9513d18..34d953edbf 100644 --- a/plugins/poll/config/locales/client.it.yml +++ b/plugins/poll/config/locales/client.it.yml @@ -54,6 +54,7 @@ it: insert: Inserisci Sondaggio help: options_count: Inserisci almeno 2 opzioni + invalid_values: Il valore minimo deve essere minore del valore massimo. poll_type: label: Tipo regular: Scelta Singola diff --git a/plugins/poll/config/locales/server.da.yml b/plugins/poll/config/locales/server.da.yml index a644ab921e..64c27602d4 100644 --- a/plugins/poll/config/locales/server.da.yml +++ b/plugins/poll/config/locales/server.da.yml @@ -38,6 +38,6 @@ da: topic_must_be_open_to_vote: "Emnet skal være åbent for at kunne stemme." poll_must_be_open_to_vote: "Afstemning skal være åben for at kunne stemme." topic_must_be_open_to_toggle_status: "Emnet skal være åbent for at ændre status." - only_staff_or_op_can_toggle_status: "Kun personalet eller emnets opretter kan ændre status for en afstemning" + only_staff_or_op_can_toggle_status: "Kun medlemmer af hjælperteamet eller emnets opretter kan ændre status for en afstemning." email: link_to_poll: "Klik for at se afstemningen." diff --git a/plugins/poll/config/locales/server.fa_IR.yml b/plugins/poll/config/locales/server.fa_IR.yml index a13b8de965..78113e2f13 100644 --- a/plugins/poll/config/locales/server.fa_IR.yml +++ b/plugins/poll/config/locales/server.fa_IR.yml @@ -7,8 +7,9 @@ fa_IR: site_settings: - poll_enabled: "به کاربران اجازه ساخت نظرسنجی ها را بده ؟ " - poll_maximum_options: "Maximum number of options allowed in a poll." + poll_enabled: "به کاربران اجازه ساخت نظرسنجی داده شود؟ " + poll_maximum_options: "حداکثر تعداد گزینه برای نظرسنجی‌ها." + poll_edit_window_mins: "چند دقیقه بعد از ارسال نظر می توان آن را ویرایش کرد" poll: multiple_polls_without_name: "چند نظرسنجی متفاوت بدون اسم است. استفاده کن از 'اسم/code>' ویژگی ٬ تا نظرسنجی منحصر به فرد را تشخیص دهد." multiple_polls_with_same_name: "چند نظرسنجی با اسم برابر وجود دارند: %{name}.استفاده کن از 'اسم/code>' ویژگی ٬ تا نظرسنجی منحصر به فرد را تشخیص دهد." @@ -23,6 +24,7 @@ fa_IR: default_poll_with_multiple_choices_has_invalid_parameters: "در نظرسنجی چند گزینه‌ای پارامترهای نامعتبری وجود دارد." named_poll_with_multiple_choices_has_invalid_parameters: "در نظرسنجی چند گزینه‌ای با نام %{name} پارامترهای نامعتبری وجود دارد." requires_at_least_1_valid_option: "You must select at least 1 valid option." + default_cannot_be_made_public: "نظر سنجی با رای را نمی توان عمومی کرد" no_polls_associated_with_this_post: "هیچ نظرسنجی با این نوشته در تماس نیستند. " no_poll_with_this_name: "هیچ نظرسنجی اسم گزاری نشده %{name} در تماس به این نوشته نیست. " post_is_deleted: "این کار را نمی‌توان روی یک نوشته حذف شده انجام داد." diff --git a/public/403.sv.html b/public/403.sv.html index cb78010cc2..e042091983 100644 --- a/public/403.sv.html +++ b/public/403.sv.html @@ -20,7 +20,7 @@

    403

    Den resursen får du inte se!

    -

    Detta kommer att ersättas med en skräddarsydd sida för Discourse 403.

    +

    Detta kommer att ersättas med en skräddarsydd 403-sida för Discourse.

    diff --git a/public/500.fa_IR.html b/public/500.fa_IR.html index cb0254098b..e38f27ec62 100644 --- a/public/500.fa_IR.html +++ b/public/500.fa_IR.html @@ -7,6 +7,8 @@

    اِی بابا

    نرم‌افزاری که ا به ین انجمن گفتگو را قدرت می‌دهد، با مشکلی غیرمنتظره روبرو شده است. از اینکه به زحمت افتادید پوزش میخواهیم.

    اطلاعات کامل در مورد این خطا، به سیستم ارسال شد و اطلاع رسانی به صورت خودکار تولید شد. ما آن را برسی می نماییم.

    -

    No further action is necessary. However, if the error condition persists, you can provide additional detail, including steps to reproduce the error, by posting a discussion topic in the site's feedback category.

    +

    هیچ اقدام دیگری لازم نیست انجام دهید . با این وجود اگر خطا + همچنان باقی است , شما می توانید جزئیات آن را به همراه گام +های لازم برای تولید خطایی مثل این ,در یک موضوع جدید در دسته بازخوردهای سایت ارسال نمایید .

    diff --git a/public/javascripts/jquery.magnific-popup-min.js b/public/javascripts/jquery.magnific-popup-min.js deleted file mode 100644 index 3124853033..0000000000 --- a/public/javascripts/jquery.magnific-popup-min.js +++ /dev/null @@ -1,3 +0,0 @@ -// Magnific Popup v0.9.9 by Dmitry Semenov -// http://bit.ly/magnific-popup#build=image -(function(a){var b="Close",c="BeforeClose",d="AfterClose",e="BeforeAppend",f="MarkupParse",g="Open",h="Change",i="mfp",j="."+i,k="mfp-ready",l="mfp-removing",m="mfp-prevent-close",n,o=function(){},p=!!window.jQuery,q,r=a(window),s,t,u,v,w,x=function(a,b){n.ev.on(i+a+j,b)},y=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d&&(f.innerHTML=d),e?c&&c.appendChild(f):(f=a(f),c&&f.appendTo(c)),f},z=function(b,c){n.ev.triggerHandler(i+b,c),n.st.callbacks&&(b=b.charAt(0).toLowerCase()+b.slice(1),n.st.callbacks[b]&&n.st.callbacks[b].apply(n,a.isArray(c)?c:[c]))},A=function(b){if(b!==w||!n.currTemplate.closeBtn)n.currTemplate.closeBtn=a(n.st.closeMarkup.replace("%title%",n.st.tClose)),w=b;return n.currTemplate.closeBtn},B=function(){a.magnificPopup.instance||(n=new o,n.init(),a.magnificPopup.instance=n)},C=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(a.transition!==undefined)return!0;while(b.length)if(b.pop()+"Transition"in a)return!0;return!1};o.prototype={constructor:o,init:function(){var b=navigator.appVersion;n.isIE7=b.indexOf("MSIE 7.")!==-1,n.isIE8=b.indexOf("MSIE 8.")!==-1,n.isLowIE=n.isIE7||n.isIE8,n.isAndroid=/android/gi.test(b),n.isIOS=/iphone|ipad|ipod/gi.test(b),n.supportsTransition=C(),n.probablyMobile=n.isAndroid||n.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),s=a(document.body),t=a(document),n.popupsCache={}},open:function(b){var c;if(b.isObj===!1){n.items=b.items.toArray(),n.index=0;var d=b.items,e;for(c=0;c(a||r.height())},_setFocus:function(){(n.st.focus?n.content.find(n.st.focus).eq(0):n.wrap).focus()},_onFocusIn:function(b){if(b.target!==n.wrap[0]&&!a.contains(n.wrap[0],b.target))return n._setFocus(),!1},_parseMarkup:function(b,c,d){var e;d.data&&(c=a.extend(d.data,c)),z(f,[b,c,d]),a.each(c,function(a,c){if(c===undefined||c===!1)return!0;e=a.split("_");if(e.length>1){var d=b.find(j+"-"+e[0]);if(d.length>0){var f=e[1];f==="replaceWith"?d[0]!==c[0]&&d.replaceWith(c):f==="img"?d.is("img")?d.attr("src",c):d.replaceWith(''):d.attr(e[1],c)}}else b.find(j+"-"+a).html(c)})},_getScrollbarSize:function(){if(n.scrollbarSize===undefined){var a=document.createElement("div");a.id="mfp-sbm",a.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(a),n.scrollbarSize=a.offsetWidth-a.clientWidth,document.body.removeChild(a)}return n.scrollbarSize}},a.magnificPopup={instance:null,proto:o.prototype,modules:[],open:function(b,c){return B(),b?b=a.extend(!0,{},b):b={},b.isObj=!0,b.index=c||0,this.instance.open(b)},close:function(){return a.magnificPopup.instance&&a.magnificPopup.instance.close()},registerModule:function(b,c){c.options&&(a.magnificPopup.defaults[b]=c.options),a.extend(this.proto,c.proto),this.modules.push(b)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'',tClose:"Close (Esc)",tLoading:"Loading..."}},a.fn.magnificPopup=function(b){B();var c=a(this);if(typeof b=="string")if(b==="open"){var d,e=p?c.data("magnificPopup"):c[0].magnificPopup,f=parseInt(arguments[1],10)||0;e.items?d=e.items[f]:(d=c,e.delegate&&(d=d.find(e.delegate)),d=d.eq(f)),n._openClick({mfpEl:d},c,e)}else n.isOpen&&n[b].apply(n,Array.prototype.slice.call(arguments,1));else b=a.extend(!0,{},b),p?c.data("magnificPopup",b):c[0].magnificPopup=b,n.addGroup(c,b);return c};var D,E=function(b){if(b.data&&b.data.title!==undefined)return b.data.title;var c=n.st.image.titleSrc;if(c){if(a.isFunction(c))return c.call(n,b);if(b.el)return b.el.attr(c)||""}return""};a.magnificPopup.registerModule("image",{options:{markup:'
    ',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'The image could not be loaded.'},proto:{initImage:function(){var a=n.st.image,c=".image";n.types.push("image"),x(g+c,function(){n.currItem.type==="image"&&a.cursor&&s.addClass(a.cursor)}),x(b+c,function(){a.cursor&&s.removeClass(a.cursor),r.off("resize"+j)}),x("Resize"+c,n.resizeImage),n.isLowIE&&x("AfterChange",n.resizeImage)},resizeImage:function(){var a=n.currItem;if(!a||!a.img)return;if(n.st.image.verticalFit){var b=0;n.isLowIE&&(b=parseInt(a.img.css("padding-top"),10)+parseInt(a.img.css("padding-bottom"),10)),a.img.css("max-height",n.wH-b)}},_onImageHasSize:function(a){a.img&&(a.hasSize=!0,D&&clearInterval(D),a.isCheckingImgSize=!1,z("ImageHasSize",a),a.imgHidden&&(n.content&&n.content.removeClass("mfp-loading"),a.imgHidden=!1))},findImageSize:function(a){var b=0,c=a.img[0],d=function(e){D&&clearInterval(D),D=setInterval(function(){if(c.naturalWidth>0){n._onImageHasSize(a);return}b>200&&clearInterval(D),b++,b===3?d(10):b===40?d(50):b===100&&d(500)},e)};d(1)},getImage:function(b,c){var d=0,e=function(){b&&(b.img[0].complete?(b.img.off(".mfploader"),b===n.currItem&&(n._onImageHasSize(b),n.updateStatus("ready")),b.hasSize=!0,b.loaded=!0,z("ImageLoadComplete")):(d++,d<200?setTimeout(e,100):f()))},f=function(){b&&(b.img.off(".mfploader"),b===n.currItem&&(n._onImageHasSize(b),n.updateStatus("error",g.tError.replace("%url%",b.src))),b.hasSize=!0,b.loaded=!0,b.loadError=!0)},g=n.st.image,h=c.find(".mfp-img");if(h.length){var i=document.createElement("img");i.className="mfp-img",b.img=a(i).on("load.mfploader",e).on("error.mfploader",f),i.src=b.src,h.is("img")&&(b.img=b.img.clone()),i=b.img[0],i.naturalWidth>0?b.hasSize=!0:i.width||(b.hasSize=!1)}return n._parseMarkup(c,{title:E(b),img_replaceWith:b.img},b),n.resizeImage(),b.hasSize?(D&&clearInterval(D),b.loadError?(c.addClass("mfp-loading"),n.updateStatus("error",g.tError.replace("%url%",b.src))):(c.removeClass("mfp-loading"),n.updateStatus("ready")),c):(n.updateStatus("loading"),b.loading=!0,b.hasSize||(b.imgHidden=!0,c.addClass("mfp-loading"),n.findImageSize(b)),c)}}});var F,G=function(){return F===undefined&&(F=document.createElement("p").style.MozTransform!==undefined),F};a.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(a){return a.is("img")?a:a.find("img")}},proto:{initZoom:function(){var a=n.st.zoom,d=".zoom",e;if(!a.enabled||!n.supportsTransition)return;var f=a.duration,g=function(b){var c=b.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),d="all "+a.duration/1e3+"s "+a.easing,e={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},f="transition";return e["-webkit-"+f]=e["-moz-"+f]=e["-o-"+f]=e[f]=d,c.css(e),c},h=function(){n.content.css("visibility","visible")},i,j;x("BuildControls"+d,function(){if(n._allowZoom()){clearTimeout(i),n.content.css("visibility","hidden"),e=n._getItemToZoom();if(!e){h();return}j=g(e),j.css(n._getOffset()),n.wrap.append(j),i=setTimeout(function(){j.css(n._getOffset(!0)),i=setTimeout(function(){h(),setTimeout(function(){j.remove(),e=j=null,z("ZoomAnimationEnded")},16)},f)},16)}}),x(c+d,function(){if(n._allowZoom()){clearTimeout(i),n.st.removalDelay=f;if(!e){e=n._getItemToZoom();if(!e)return;j=g(e)}j.css(n._getOffset(!0)),n.wrap.append(j),n.content.css("visibility","hidden"),setTimeout(function(){j.css(n._getOffset())},16)}}),x(b+d,function(){n._allowZoom()&&(h(),j&&j.remove(),e=null)})},_allowZoom:function(){return n.currItem.type==="image"},_getItemToZoom:function(){return n.currItem.hasSize?n.currItem.img:!1},_getOffset:function(b){var c;b?c=n.currItem.img:c=n.st.zoom.opener(n.currItem.el||n.currItem);var d=c.offset(),e=parseInt(c.css("padding-top"),10),f=parseInt(c.css("padding-bottom"),10);d.top-=a(window).scrollTop()-e;var g={width:c.width(),height:(p?c.innerHeight():c[0].offsetHeight)-f-e};return G()?g["-moz-transform"]=g.transform="translate("+d.left+"px,"+d.top+"px)":(g.left=d.left,g.top=d.top),g}}}),B()})(window.jQuery||window.Zepto) diff --git a/public/javascripts/jquery.magnific-popup.min.js b/public/javascripts/jquery.magnific-popup.min.js new file mode 100644 index 0000000000..6ee3a3bd5b --- /dev/null +++ b/public/javascripts/jquery.magnific-popup.min.js @@ -0,0 +1,4 @@ +/*! Magnific Popup - v1.1.0 - 2016-02-20 +* http://dimsemenov.com/plugins/magnific-popup/ +* Copyright (c) 2016 Dmitry Semenov; */ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):window.jQuery||window.Zepto)}(function(a){var b,c,d,e,f,g,h="Close",i="BeforeClose",j="AfterClose",k="BeforeAppend",l="MarkupParse",m="Open",n="Change",o="mfp",p="."+o,q="mfp-ready",r="mfp-removing",s="mfp-prevent-close",t=function(){},u=!!window.jQuery,v=a(window),w=function(a,c){b.ev.on(o+a+p,c)},x=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d&&(f.innerHTML=d),e?c&&c.appendChild(f):(f=a(f),c&&f.appendTo(c)),f},y=function(c,d){b.ev.triggerHandler(o+c,d),b.st.callbacks&&(c=c.charAt(0).toLowerCase()+c.slice(1),b.st.callbacks[c]&&b.st.callbacks[c].apply(b,a.isArray(d)?d:[d]))},z=function(c){return c===g&&b.currTemplate.closeBtn||(b.currTemplate.closeBtn=a(b.st.closeMarkup.replace("%title%",b.st.tClose)),g=c),b.currTemplate.closeBtn},A=function(){a.magnificPopup.instance||(b=new t,b.init(),a.magnificPopup.instance=b)},B=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(void 0!==a.transition)return!0;for(;b.length;)if(b.pop()+"Transition"in a)return!0;return!1};t.prototype={constructor:t,init:function(){var c=navigator.appVersion;b.isLowIE=b.isIE8=document.all&&!document.addEventListener,b.isAndroid=/android/gi.test(c),b.isIOS=/iphone|ipad|ipod/gi.test(c),b.supportsTransition=B(),b.probablyMobile=b.isAndroid||b.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),d=a(document),b.popupsCache={}},open:function(c){var e;if(c.isObj===!1){b.items=c.items.toArray(),b.index=0;var g,h=c.items;for(e=0;e(a||v.height())},_setFocus:function(){(b.st.focus?b.content.find(b.st.focus).eq(0):b.wrap).focus()},_onFocusIn:function(c){return c.target===b.wrap[0]||a.contains(b.wrap[0],c.target)?void 0:(b._setFocus(),!1)},_parseMarkup:function(b,c,d){var e;d.data&&(c=a.extend(d.data,c)),y(l,[b,c,d]),a.each(c,function(c,d){if(void 0===d||d===!1)return!0;if(e=c.split("_"),e.length>1){var f=b.find(p+"-"+e[0]);if(f.length>0){var g=e[1];"replaceWith"===g?f[0]!==d[0]&&f.replaceWith(d):"img"===g?f.is("img")?f.attr("src",d):f.replaceWith(a("").attr("src",d).attr("class",f.attr("class"))):f.attr(e[1],d)}}else b.find(p+"-"+c).html(d)})},_getScrollbarSize:function(){if(void 0===b.scrollbarSize){var a=document.createElement("div");a.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(a),b.scrollbarSize=a.offsetWidth-a.clientWidth,document.body.removeChild(a)}return b.scrollbarSize}},a.magnificPopup={instance:null,proto:t.prototype,modules:[],open:function(b,c){return A(),b=b?a.extend(!0,{},b):{},b.isObj=!0,b.index=c||0,this.instance.open(b)},close:function(){return a.magnificPopup.instance&&a.magnificPopup.instance.close()},registerModule:function(b,c){c.options&&(a.magnificPopup.defaults[b]=c.options),a.extend(this.proto,c.proto),this.modules.push(b)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'',tClose:"Close (Esc)",tLoading:"Loading...",autoFocusLast:!0}},a.fn.magnificPopup=function(c){A();var d=a(this);if("string"==typeof c)if("open"===c){var e,f=u?d.data("magnificPopup"):d[0].magnificPopup,g=parseInt(arguments[1],10)||0;f.items?e=f.items[g]:(e=d,f.delegate&&(e=e.find(f.delegate)),e=e.eq(g)),b._openClick({mfpEl:e},d,f)}else b.isOpen&&b[c].apply(b,Array.prototype.slice.call(arguments,1));else c=a.extend(!0,{},c),u?d.data("magnificPopup",c):d[0].magnificPopup=c,b.addGroup(d,c);return d};var C,D,E,F="inline",G=function(){E&&(D.after(E.addClass(C)).detach(),E=null)};a.magnificPopup.registerModule(F,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){b.types.push(F),w(h+"."+F,function(){G()})},getInline:function(c,d){if(G(),c.src){var e=b.st.inline,f=a(c.src);if(f.length){var g=f[0].parentNode;g&&g.tagName&&(D||(C=e.hiddenClass,D=x(C),C="mfp-"+C),E=f.after(D).detach().removeClass(C)),b.updateStatus("ready")}else b.updateStatus("error",e.tNotFound),f=a("
    ");return c.inlineElement=f,f}return b.updateStatus("ready"),b._parseMarkup(d,{},c),d}}});var H,I="ajax",J=function(){H&&a(document.body).removeClass(H)},K=function(){J(),b.req&&b.req.abort()};a.magnificPopup.registerModule(I,{options:{settings:null,cursor:"mfp-ajax-cur",tError:'The content could not be loaded.'},proto:{initAjax:function(){b.types.push(I),H=b.st.ajax.cursor,w(h+"."+I,K),w("BeforeChange."+I,K)},getAjax:function(c){H&&a(document.body).addClass(H),b.updateStatus("loading");var d=a.extend({url:c.src,success:function(d,e,f){var g={data:d,xhr:f};y("ParseAjax",g),b.appendContent(a(g.data),I),c.finished=!0,J(),b._setFocus(),setTimeout(function(){b.wrap.addClass(q)},16),b.updateStatus("ready"),y("AjaxContentAdded")},error:function(){J(),c.finished=c.loadError=!0,b.updateStatus("error",b.st.ajax.tError.replace("%url%",c.src))}},b.st.ajax.settings);return b.req=a.ajax(d),""}}});var L,M=function(c){if(c.data&&void 0!==c.data.title)return c.data.title;var d=b.st.image.titleSrc;if(d){if(a.isFunction(d))return d.call(b,c);if(c.el)return c.el.attr(d)||""}return""};a.magnificPopup.registerModule("image",{options:{markup:'
    ',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'The image could not be loaded.'},proto:{initImage:function(){var c=b.st.image,d=".image";b.types.push("image"),w(m+d,function(){"image"===b.currItem.type&&c.cursor&&a(document.body).addClass(c.cursor)}),w(h+d,function(){c.cursor&&a(document.body).removeClass(c.cursor),v.off("resize"+p)}),w("Resize"+d,b.resizeImage),b.isLowIE&&w("AfterChange",b.resizeImage)},resizeImage:function(){var a=b.currItem;if(a&&a.img&&b.st.image.verticalFit){var c=0;b.isLowIE&&(c=parseInt(a.img.css("padding-top"),10)+parseInt(a.img.css("padding-bottom"),10)),a.img.css("max-height",b.wH-c)}},_onImageHasSize:function(a){a.img&&(a.hasSize=!0,L&&clearInterval(L),a.isCheckingImgSize=!1,y("ImageHasSize",a),a.imgHidden&&(b.content&&b.content.removeClass("mfp-loading"),a.imgHidden=!1))},findImageSize:function(a){var c=0,d=a.img[0],e=function(f){L&&clearInterval(L),L=setInterval(function(){return d.naturalWidth>0?void b._onImageHasSize(a):(c>200&&clearInterval(L),c++,void(3===c?e(10):40===c?e(50):100===c&&e(500)))},f)};e(1)},getImage:function(c,d){var e=0,f=function(){c&&(c.img[0].complete?(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("ready")),c.hasSize=!0,c.loaded=!0,y("ImageLoadComplete")):(e++,200>e?setTimeout(f,100):g()))},g=function(){c&&(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("error",h.tError.replace("%url%",c.src))),c.hasSize=!0,c.loaded=!0,c.loadError=!0)},h=b.st.image,i=d.find(".mfp-img");if(i.length){var j=document.createElement("img");j.className="mfp-img",c.el&&c.el.find("img").length&&(j.alt=c.el.find("img").attr("alt")),c.img=a(j).on("load.mfploader",f).on("error.mfploader",g),j.src=c.src,i.is("img")&&(c.img=c.img.clone()),j=c.img[0],j.naturalWidth>0?c.hasSize=!0:j.width||(c.hasSize=!1)}return b._parseMarkup(d,{title:M(c),img_replaceWith:c.img},c),b.resizeImage(),c.hasSize?(L&&clearInterval(L),c.loadError?(d.addClass("mfp-loading"),b.updateStatus("error",h.tError.replace("%url%",c.src))):(d.removeClass("mfp-loading"),b.updateStatus("ready")),d):(b.updateStatus("loading"),c.loading=!0,c.hasSize||(c.imgHidden=!0,d.addClass("mfp-loading"),b.findImageSize(c)),d)}}});var N,O=function(){return void 0===N&&(N=void 0!==document.createElement("p").style.MozTransform),N};a.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(a){return a.is("img")?a:a.find("img")}},proto:{initZoom:function(){var a,c=b.st.zoom,d=".zoom";if(c.enabled&&b.supportsTransition){var e,f,g=c.duration,j=function(a){var b=a.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),d="all "+c.duration/1e3+"s "+c.easing,e={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},f="transition";return e["-webkit-"+f]=e["-moz-"+f]=e["-o-"+f]=e[f]=d,b.css(e),b},k=function(){b.content.css("visibility","visible")};w("BuildControls"+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.content.css("visibility","hidden"),a=b._getItemToZoom(),!a)return void k();f=j(a),f.css(b._getOffset()),b.wrap.append(f),e=setTimeout(function(){f.css(b._getOffset(!0)),e=setTimeout(function(){k(),setTimeout(function(){f.remove(),a=f=null,y("ZoomAnimationEnded")},16)},g)},16)}}),w(i+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.st.removalDelay=g,!a){if(a=b._getItemToZoom(),!a)return;f=j(a)}f.css(b._getOffset(!0)),b.wrap.append(f),b.content.css("visibility","hidden"),setTimeout(function(){f.css(b._getOffset())},16)}}),w(h+d,function(){b._allowZoom()&&(k(),f&&f.remove(),a=null)})}},_allowZoom:function(){return"image"===b.currItem.type},_getItemToZoom:function(){return b.currItem.hasSize?b.currItem.img:!1},_getOffset:function(c){var d;d=c?b.currItem.img:b.st.zoom.opener(b.currItem.el||b.currItem);var e=d.offset(),f=parseInt(d.css("padding-top"),10),g=parseInt(d.css("padding-bottom"),10);e.top-=a(window).scrollTop()-f;var h={width:d.width(),height:(u?d.innerHeight():d[0].offsetHeight)-g-f};return O()?h["-moz-transform"]=h.transform="translate("+e.left+"px,"+e.top+"px)":(h.left=e.left,h.top=e.top),h}}});var P="iframe",Q="//about:blank",R=function(a){if(b.currTemplate[P]){var c=b.currTemplate[P].find("iframe");c.length&&(a||(c[0].src=Q),b.isIE8&&c.css("display",a?"block":"none"))}};a.magnificPopup.registerModule(P,{options:{markup:'
    ',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){b.types.push(P),w("BeforeChange",function(a,b,c){b!==c&&(b===P?R():c===P&&R(!0))}),w(h+"."+P,function(){R()})},getIframe:function(c,d){var e=c.src,f=b.st.iframe;a.each(f.patterns,function(){return e.indexOf(this.index)>-1?(this.id&&(e="string"==typeof this.id?e.substr(e.lastIndexOf(this.id)+this.id.length,e.length):this.id.call(this,e)),e=this.src.replace("%id%",e),!1):void 0});var g={};return f.srcAction&&(g[f.srcAction]=e),b._parseMarkup(d,g,c),b.updateStatus("ready"),d}}});var S=function(a){var c=b.items.length;return a>c-1?a-c:0>a?c+a:a},T=function(a,b,c){return a.replace(/%curr%/gi,b+1).replace(/%total%/gi,c)};a.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var c=b.st.gallery,e=".mfp-gallery";return b.direction=!0,c&&c.enabled?(f+=" mfp-gallery",w(m+e,function(){c.navigateByImgClick&&b.wrap.on("click"+e,".mfp-img",function(){return b.items.length>1?(b.next(),!1):void 0}),d.on("keydown"+e,function(a){37===a.keyCode?b.prev():39===a.keyCode&&b.next()})}),w("UpdateStatus"+e,function(a,c){c.text&&(c.text=T(c.text,b.currItem.index,b.items.length))}),w(l+e,function(a,d,e,f){var g=b.items.length;e.counter=g>1?T(c.tCounter,f.index,g):""}),w("BuildControls"+e,function(){if(b.items.length>1&&c.arrows&&!b.arrowLeft){var d=c.arrowMarkup,e=b.arrowLeft=a(d.replace(/%title%/gi,c.tPrev).replace(/%dir%/gi,"left")).addClass(s),f=b.arrowRight=a(d.replace(/%title%/gi,c.tNext).replace(/%dir%/gi,"right")).addClass(s);e.click(function(){b.prev()}),f.click(function(){b.next()}),b.container.append(e.add(f))}}),w(n+e,function(){b._preloadTimeout&&clearTimeout(b._preloadTimeout),b._preloadTimeout=setTimeout(function(){b.preloadNearbyImages(),b._preloadTimeout=null},16)}),void w(h+e,function(){d.off(e),b.wrap.off("click"+e),b.arrowRight=b.arrowLeft=null})):!1},next:function(){b.direction=!0,b.index=S(b.index+1),b.updateItemHTML()},prev:function(){b.direction=!1,b.index=S(b.index-1),b.updateItemHTML()},goTo:function(a){b.direction=a>=b.index,b.index=a,b.updateItemHTML()},preloadNearbyImages:function(){var a,c=b.st.gallery.preload,d=Math.min(c[0],b.items.length),e=Math.min(c[1],b.items.length);for(a=1;a<=(b.direction?e:d);a++)b._preloadItem(b.index+a);for(a=1;a<=(b.direction?d:e);a++)b._preloadItem(b.index-a)},_preloadItem:function(c){if(c=S(c),!b.items[c].preloaded){var d=b.items[c];d.parsed||(d=b.parseEl(c)),y("LazyLoad",d),"image"===d.type&&(d.img=a('').on("load.mfploader",function(){d.hasSize=!0}).on("error.mfploader",function(){d.hasSize=!0,d.loadError=!0,y("LazyLoadError",d)}).attr("src",d.src)),d.preloaded=!0}}}});var U="retina";a.magnificPopup.registerModule(U,{options:{replaceSrc:function(a){return a.src.replace(/\.\w+$/,function(a){return"@2x"+a})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var a=b.st.retina,c=a.ratio;c=isNaN(c)?c():c,c>1&&(w("ImageHasSize."+U,function(a,b){b.img.css({"max-width":b.img[0].naturalWidth/c,width:"100%"})}),w("ElementParse."+U,function(b,d){d.src=a.replaceSrc(d,c)}))}}}}),A()}); \ No newline at end of file diff --git a/public/javascripts/spectrum.css b/public/javascripts/spectrum.css new file mode 100644 index 0000000000..a8ad9e4f82 --- /dev/null +++ b/public/javascripts/spectrum.css @@ -0,0 +1,507 @@ +/*** +Spectrum Colorpicker v1.8.0 +https://github.com/bgrins/spectrum +Author: Brian Grinstead +License: MIT +***/ + +.sp-container { + position:absolute; + top:0; + left:0; + display:inline-block; + *display: inline; + *zoom: 1; + /* https://github.com/bgrins/spectrum/issues/40 */ + z-index: 9999994; + overflow: hidden; +} +.sp-container.sp-flat { + position: relative; +} + +/* Fix for * { box-sizing: border-box; } */ +.sp-container, +.sp-container * { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */ +.sp-top { + position:relative; + width: 100%; + display:inline-block; +} +.sp-top-inner { + position:absolute; + top:0; + left:0; + bottom:0; + right:0; +} +.sp-color { + position: absolute; + top:0; + left:0; + bottom:0; + right:20%; +} +.sp-hue { + position: absolute; + top:0; + right:0; + bottom:0; + left:84%; + height: 100%; +} + +.sp-clear-enabled .sp-hue { + top:33px; + height: 77.5%; +} + +.sp-fill { + padding-top: 80%; +} +.sp-sat, .sp-val { + position: absolute; + top:0; + left:0; + right:0; + bottom:0; +} + +.sp-alpha-enabled .sp-top { + margin-bottom: 18px; +} +.sp-alpha-enabled .sp-alpha { + display: block; +} +.sp-alpha-handle { + position:absolute; + top:-4px; + bottom: -4px; + width: 6px; + left: 50%; + cursor: pointer; + border: 1px solid black; + background: white; + opacity: .8; +} +.sp-alpha { + display: none; + position: absolute; + bottom: -14px; + right: 0; + left: 0; + height: 8px; +} +.sp-alpha-inner { + border: solid 1px #333; +} + +.sp-clear { + display: none; +} + +.sp-clear.sp-clear-display { + background-position: center; +} + +.sp-clear-enabled .sp-clear { + display: block; + position:absolute; + top:0px; + right:0; + bottom:0; + left:84%; + height: 28px; +} + +/* Don't allow text selection */ +.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button { + -webkit-user-select:none; + -moz-user-select: -moz-none; + -o-user-select:none; + user-select: none; +} + +.sp-container.sp-input-disabled .sp-input-container { + display: none; +} +.sp-container.sp-buttons-disabled .sp-button-container { + display: none; +} +.sp-container.sp-palette-buttons-disabled .sp-palette-button-container { + display: none; +} +.sp-palette-only .sp-picker-container { + display: none; +} +.sp-palette-disabled .sp-palette-container { + display: none; +} + +.sp-initial-disabled .sp-initial { + display: none; +} + + +/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */ +.sp-sat { + background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0))); + background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0)); + background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0)); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)"; + filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81'); +} +.sp-val { + background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0))); + background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0)); + background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0)); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)"; + filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000'); +} + +.sp-hue { + background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000)); + background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); +} + +/* IE filters do not support multiple color stops. + Generate 6 divs, line them up, and do two color gradients for each. + Yes, really. + */ +.sp-1 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00'); +} +.sp-2 { + height:16%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00'); +} +.sp-3 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff'); +} +.sp-4 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff'); +} +.sp-5 { + height:16%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff'); +} +.sp-6 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000'); +} + +.sp-hidden { + display: none !important; +} + +/* Clearfix hack */ +.sp-cf:before, .sp-cf:after { content: ""; display: table; } +.sp-cf:after { clear: both; } +.sp-cf { *zoom: 1; } + +/* Mobile devices, make hue slider bigger so it is easier to slide */ +@media (max-device-width: 480px) { + .sp-color { right: 40%; } + .sp-hue { left: 63%; } + .sp-fill { padding-top: 60%; } +} +.sp-dragger { + border-radius: 5px; + height: 5px; + width: 5px; + border: 1px solid #fff; + background: #000; + cursor: pointer; + position:absolute; + top:0; + left: 0; +} +.sp-slider { + position: absolute; + top:0; + cursor:pointer; + height: 3px; + left: -1px; + right: -1px; + border: 1px solid #000; + background: white; + opacity: .8; +} + +/* +Theme authors: +Here are the basic themeable display options (colors, fonts, global widths). +See http://bgrins.github.io/spectrum/themes/ for instructions. +*/ + +.sp-container { + border-radius: 0; + background-color: #ECECEC; + border: solid 1px #f0c49B; + padding: 0; +} +.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear { + font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} +.sp-top { + margin-bottom: 3px; +} +.sp-color, .sp-hue, .sp-clear { + border: solid 1px #666; +} + +/* Input */ +.sp-input-container { + float:right; + width: 100px; + margin-bottom: 4px; +} +.sp-initial-disabled .sp-input-container { + width: 100%; +} +.sp-input { + font-size: 12px !important; + border: 1px inset; + padding: 4px 5px; + margin: 0; + width: 100%; + background:transparent; + border-radius: 3px; + color: #222; +} +.sp-input:focus { + border: 1px solid orange; +} +.sp-input.sp-validation-error { + border: 1px solid red; + background: #fdd; +} +.sp-picker-container , .sp-palette-container { + float:left; + position: relative; + padding: 10px; + padding-bottom: 300px; + margin-bottom: -290px; +} +.sp-picker-container { + width: 172px; + border-left: solid 1px #fff; +} + +/* Palettes */ +.sp-palette-container { + border-right: solid 1px #ccc; +} + +.sp-palette-only .sp-palette-container { + border: 0; +} + +.sp-palette .sp-thumb-el { + display: block; + position:relative; + float:left; + width: 24px; + height: 15px; + margin: 3px; + cursor: pointer; + border:solid 2px transparent; +} +.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active { + border-color: orange; +} +.sp-thumb-el { + position:relative; +} + +/* Initial */ +.sp-initial { + float: left; + border: solid 1px #333; +} +.sp-initial span { + width: 30px; + height: 25px; + border:none; + display:block; + float:left; + margin:0; +} + +.sp-initial .sp-clear-display { + background-position: center; +} + +/* Buttons */ +.sp-palette-button-container, +.sp-button-container { + float: right; +} + +/* Replacer (the little preview div that shows up instead of the ) */ +.sp-replacer { + margin:0; + overflow:hidden; + cursor:pointer; + padding: 4px; + display:inline-block; + *zoom: 1; + *display: inline; + border: solid 1px #91765d; + background: #eee; + color: #333; + vertical-align: middle; +} +.sp-replacer:hover, .sp-replacer.sp-active { + border-color: #F0C49B; + color: #111; +} +.sp-replacer.sp-disabled { + cursor:default; + border-color: silver; + color: silver; +} +.sp-dd { + padding: 2px 0; + height: 16px; + line-height: 16px; + float:left; + font-size:10px; +} +.sp-preview { + position:relative; + width:25px; + height: 20px; + border: solid 1px #222; + margin-right: 5px; + float:left; + z-index: 0; +} + +.sp-palette { + *width: 220px; + max-width: 220px; +} +.sp-palette .sp-thumb-el { + width:16px; + height: 16px; + margin:2px 1px; + border: solid 1px #d0d0d0; +} + +.sp-container { + padding-bottom:0; +} + + +/* Buttons: http://hellohappy.org/css3-buttons/ */ +.sp-container button { + background-color: #eeeeee; + background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); + background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); + background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); + background-image: -o-linear-gradient(top, #eeeeee, #cccccc); + background-image: linear-gradient(to bottom, #eeeeee, #cccccc); + border: 1px solid #ccc; + border-bottom: 1px solid #bbb; + border-radius: 3px; + color: #333; + font-size: 14px; + line-height: 1; + padding: 5px 4px; + text-align: center; + text-shadow: 0 1px 0 #eee; + vertical-align: middle; +} +.sp-container button:hover { + background-color: #dddddd; + background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); + background-image: linear-gradient(to bottom, #dddddd, #bbbbbb); + border: 1px solid #bbb; + border-bottom: 1px solid #999; + cursor: pointer; + text-shadow: 0 1px 0 #ddd; +} +.sp-container button:active { + border: 1px solid #aaa; + border-bottom: 1px solid #888; + -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; +} +.sp-cancel { + font-size: 11px; + color: #d93f3f !important; + margin:0; + padding:2px; + margin-right: 5px; + vertical-align: middle; + text-decoration:none; + +} +.sp-cancel:hover { + color: #d93f3f !important; + text-decoration: underline; +} + + +.sp-palette span:hover, .sp-palette span.sp-thumb-active { + border-color: #000; +} + +.sp-preview, .sp-alpha, .sp-thumb-el { + position:relative; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); +} +.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner { + display:block; + position:absolute; + top:0;left:0;bottom:0;right:0; +} + +.sp-palette .sp-thumb-inner { + background-position: 50% 50%; + background-repeat: no-repeat; +} + +.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=); +} + +.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=); +} + +.sp-clear-display { + background-repeat:no-repeat; + background-position: center; + background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==); +} diff --git a/public/javascripts/spectrum.js b/public/javascripts/spectrum.js new file mode 100644 index 0000000000..2b31452841 --- /dev/null +++ b/public/javascripts/spectrum.js @@ -0,0 +1,2 @@ +(function(factory){"use strict";if(typeof define==="function"&&define.amd){define(["jquery"],factory)}else if(typeof exports=="object"&&typeof module=="object"){module.exports=factory(require("jquery"))}else{factory(jQuery)}})(function($,undefined){"use strict";var defaultOpts={beforeShow:noop,move:noop,change:noop,show:noop,hide:noop,color:false,flat:false,showInput:false,allowEmpty:false,showButtons:true,clickoutFiresChange:true,showInitial:false,showPalette:false,showPaletteOnly:false,hideAfterPaletteSelect:false,togglePaletteOnly:false,showSelectionPalette:true,localStorageKey:false,appendTo:"body",maxSelectionSize:7,cancelText:"cancel",chooseText:"choose",togglePaletteMoreText:"more",togglePaletteLessText:"less",clearText:"Clear Color Selection",noColorSelectedText:"No Color Selected",preferredFormat:false,className:"",containerClassName:"",replacerClassName:"",showAlpha:false,theme:"sp-light",palette:[["#ffffff","#000000","#ff0000","#ff8000","#ffff00","#008000","#0000ff","#4b0082","#9400d3"]],selectionPalette:[],disabled:false,offset:null},spectrums=[],IE=!!/msie/i.exec(window.navigator.userAgent),rgbaSupport=function(){function contains(str,substr){return!!~(""+str).indexOf(substr)}var elem=document.createElement("div");var style=elem.style;style.cssText="background-color:rgba(0,0,0,.5)";return contains(style.backgroundColor,"rgba")||contains(style.backgroundColor,"hsla")}(),replaceInput=["
    ","
    ","
    ","
    "].join(""),markup=function(){var gradientFix="";if(IE){for(var i=1;i<=6;i++){gradientFix+="
    "}}return["
    ","
    ","
    ","
    ","","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ","
    ",gradientFix,"
    ","
    ","
    ","
    ","
    ","","
    ","
    ","
    ","","","
    ","
    ","
    "].join("")}();function paletteTemplate(p,color,className,opts){var html=[];for(var i=0;i')}else{var cls="sp-clear-display";html.push($("
    ").append($('').attr("title",opts.noColorSelectedText)).html())}}return"
    "+html.join("")+"
    "}function hideAll(){for(var i=0;iMath.abs(dragY-oldDragY);shiftMovementDirection=furtherFromX?"x":"y"}var setSaturation=!shiftMovementDirection||shiftMovementDirection==="x";var setValue=!shiftMovementDirection||shiftMovementDirection==="y";if(setSaturation){currentSaturation=parseFloat(dragX/dragWidth)}if(setValue){currentValue=parseFloat((dragHeight-dragY)/dragHeight)}isEmpty=false;if(!opts.showAlpha){currentAlpha=1}move()},dragStart,dragStop);if(!!initialColor){set(initialColor);updateUI();currentPreferredFormat=opts.preferredFormat||tinycolor(initialColor).format;addColorToSelectionPalette(initialColor)}else{updateUI()}if(flat){show()}function paletteElementClick(e){if(e.data&&e.data.ignore){set($(e.target).closest(".sp-thumb-el").data("color"));move()}else{set($(e.target).closest(".sp-thumb-el").data("color"));move();updateOriginalInput(true);if(opts.hideAfterPaletteSelect){hide()}}return false}var paletteEvent=IE?"mousedown.spectrum":"click.spectrum touchstart.spectrum";paletteContainer.delegate(".sp-thumb-el",paletteEvent,paletteElementClick);initialColorContainer.delegate(".sp-thumb-el:nth-child(1)",paletteEvent,{ignore:true},paletteElementClick)}function updateSelectionPaletteFromStorage(){if(localStorageKey&&window.localStorage){try{var oldPalette=window.localStorage[localStorageKey].split(",#");if(oldPalette.length>1){delete window.localStorage[localStorageKey];$.each(oldPalette,function(i,c){addColorToSelectionPalette(c)})}}catch(e){}try{selectionPalette=window.localStorage[localStorageKey].split(";")}catch(e){}}}function addColorToSelectionPalette(color){if(showSelectionPalette){var rgb=tinycolor(color).toRgbString();if(!paletteLookup[rgb]&&$.inArray(rgb,selectionPalette)===-1){selectionPalette.push(rgb);while(selectionPalette.length>maxSelectionSize){selectionPalette.shift()}}if(localStorageKey&&window.localStorage){try{window.localStorage[localStorageKey]=selectionPalette.join(";")}catch(e){}}}}function getUniqueSelectionPalette(){var unique=[];if(opts.showPalette){for(var i=0;iviewWidth&&viewWidth>dpWidth?Math.abs(offset.left+dpWidth-viewWidth):0);offset.top-=Math.min(offset.top,offset.top+dpHeight>viewHeight&&viewHeight>dpHeight?Math.abs(dpHeight+inputHeight-extraY):extraY);return offset}function noop(){}function stopPropagation(e){e.stopPropagation()}function bind(func,obj){var slice=Array.prototype.slice;var args=slice.call(arguments,2);return function(){return func.apply(obj,args.concat(slice.call(arguments)))}}function draggable(element,onmove,onstart,onstop){onmove=onmove||function(){};onstart=onstart||function(){};onstop=onstop||function(){};var doc=document;var dragging=false;var offset={};var maxHeight=0;var maxWidth=0;var hasTouch="ontouchstart"in window;var duringDragEvents={};duringDragEvents["selectstart"]=prevent;duringDragEvents["dragstart"]=prevent;duringDragEvents["touchmove mousemove"]=move;duringDragEvents["touchend mouseup"]=stop;function prevent(e){if(e.stopPropagation){e.stopPropagation()}if(e.preventDefault){e.preventDefault()}e.returnValue=false}function move(e){if(dragging){if(IE&&doc.documentMode<9&&!e.button){return stop()}var t0=e.originalEvent&&e.originalEvent.touches&&e.originalEvent.touches[0];var pageX=t0&&t0.pageX||e.pageX;var pageY=t0&&t0.pageY||e.pageY;var dragX=Math.max(0,Math.min(pageX-offset.left,maxWidth));var dragY=Math.max(0,Math.min(pageY-offset.top,maxHeight));if(hasTouch){prevent(e)}onmove.apply(element,[dragX,dragY,e])}}function start(e){var rightclick=e.which?e.which==3:e.button==2;if(!rightclick&&!dragging){if(onstart.apply(element,arguments)!==false){dragging=true;maxHeight=$(element).height();maxWidth=$(element).width();offset=$(element).offset();$(doc).bind(duringDragEvents);$(doc.body).addClass("sp-dragging");move(e);prevent(e)}}}function stop(){if(dragging){$(doc).unbind(duringDragEvents);$(doc.body).removeClass("sp-dragging");setTimeout(function(){onstop.apply(element,arguments)},0)}dragging=false}$(element).bind("touchstart mousedown",start)}function throttle(func,wait,debounce){var timeout;return function(){var context=this,args=arguments;var throttler=function(){timeout=null;func.apply(context,args)};if(debounce)clearTimeout(timeout);if(debounce||!timeout)timeout=setTimeout(throttler,wait)}}function inputTypeColorSupport(){return $.fn.spectrum.inputTypeColorSupport()}var dataID="spectrum.id";$.fn.spectrum=function(opts,extra){if(typeof opts=="string"){var returnValue=this;var args=Array.prototype.slice.call(arguments,1);this.each(function(){var spect=spectrums[$(this).data(dataID)];if(spect){var method=spect[opts];if(!method){throw new Error("Spectrum: no such method: '"+opts+"'")}if(opts=="get"){returnValue=spect.get()}else if(opts=="container"){returnValue=spect.container}else if(opts=="option"){returnValue=spect.option.apply(spect,args)}else if(opts=="destroy"){spect.destroy();$(this).removeData(dataID)}else{method.apply(spect,args)}}});return returnValue}return this.spectrum("destroy").each(function(){var options=$.extend({},opts,$(this).data());var spect=spectrum(this,options);$(this).data(dataID,spect.id)})};$.fn.spectrum.load=true;$.fn.spectrum.loadOpts={};$.fn.spectrum.draggable=draggable;$.fn.spectrum.defaults=defaultOpts;$.fn.spectrum.inputTypeColorSupport=function inputTypeColorSupport(){if(typeof inputTypeColorSupport._cachedResult==="undefined"){var colorInput=$("")[0];inputTypeColorSupport._cachedResult=colorInput.type==="color"&&colorInput.value!==""}return inputTypeColorSupport._cachedResult};$.spectrum={};$.spectrum.localization={};$.spectrum.palettes={};$.fn.spectrum.processNativeColorInputs=function(){var colorInputs=$("input[type=color]");if(colorInputs.length&&!inputTypeColorSupport()){colorInputs.spectrum({preferredFormat:"hex6"})}};(function(){var trimLeft=/^[\s,#]+/,trimRight=/\s+$/,tinyCounter=0,math=Math,mathRound=math.round,mathMin=math.min,mathMax=math.max,mathRandom=math.random;var tinycolor=function(color,opts){color=color?color:"";opts=opts||{};if(color instanceof tinycolor){return color}if(!(this instanceof tinycolor)){return new tinycolor(color,opts)}var rgb=inputToRGB(color);this._originalInput=color,this._r=rgb.r,this._g=rgb.g,this._b=rgb.b,this._a=rgb.a,this._roundA=mathRound(100*this._a)/100,this._format=opts.format||rgb.format;this._gradientType=opts.gradientType;if(this._r<1){this._r=mathRound(this._r)}if(this._g<1){this._g=mathRound(this._g)}if(this._b<1){this._b=mathRound(this._b)}this._ok=rgb.ok;this._tc_id=tinyCounter++};tinycolor.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var rgb=this.toRgb();return(rgb.r*299+rgb.g*587+rgb.b*114)/1e3},setAlpha:function(value){this._a=boundAlpha(value);this._roundA=mathRound(100*this._a)/100;return this},toHsv:function(){var hsv=rgbToHsv(this._r,this._g,this._b);return{h:hsv.h*360,s:hsv.s,v:hsv.v,a:this._a}},toHsvString:function(){var hsv=rgbToHsv(this._r,this._g,this._b);var h=mathRound(hsv.h*360),s=mathRound(hsv.s*100),v=mathRound(hsv.v*100);return this._a==1?"hsv("+h+", "+s+"%, "+v+"%)":"hsva("+h+", "+s+"%, "+v+"%, "+this._roundA+")"},toHsl:function(){var hsl=rgbToHsl(this._r,this._g,this._b);return{h:hsl.h*360,s:hsl.s,l:hsl.l,a:this._a}},toHslString:function(){var hsl=rgbToHsl(this._r,this._g,this._b);var h=mathRound(hsl.h*360),s=mathRound(hsl.s*100),l=mathRound(hsl.l*100);return this._a==1?"hsl("+h+", "+s+"%, "+l+"%)":"hsla("+h+", "+s+"%, "+l+"%, "+this._roundA+")"},toHex:function(allow3Char){return rgbToHex(this._r,this._g,this._b,allow3Char)},toHexString:function(allow3Char){return"#"+this.toHex(allow3Char)},toHex8:function(){return rgbaToHex(this._r,this._g,this._b,this._a)},toHex8String:function(){return"#"+this.toHex8()},toRgb:function(){return{r:mathRound(this._r),g:mathRound(this._g),b:mathRound(this._b),a:this._a}},toRgbString:function(){return this._a==1?"rgb("+mathRound(this._r)+", "+mathRound(this._g)+", "+mathRound(this._b)+")":"rgba("+mathRound(this._r)+", "+mathRound(this._g)+", "+mathRound(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:mathRound(bound01(this._r,255)*100)+"%",g:mathRound(bound01(this._g,255)*100)+"%",b:mathRound(bound01(this._b,255)*100)+"%",a:this._a}},toPercentageRgbString:function(){return this._a==1?"rgb("+mathRound(bound01(this._r,255)*100)+"%, "+mathRound(bound01(this._g,255)*100)+"%, "+mathRound(bound01(this._b,255)*100)+"%)":"rgba("+mathRound(bound01(this._r,255)*100)+"%, "+mathRound(bound01(this._g,255)*100)+"%, "+mathRound(bound01(this._b,255)*100)+"%, "+this._roundA+")"},toName:function(){if(this._a===0){return"transparent"}if(this._a<1){return false}return hexNames[rgbToHex(this._r,this._g,this._b,true)]||false},toFilter:function(secondColor){var hex8String="#"+rgbaToHex(this._r,this._g,this._b,this._a);var secondHex8String=hex8String;var gradientType=this._gradientType?"GradientType = 1, ":"";if(secondColor){var s=tinycolor(secondColor);secondHex8String=s.toHex8String()}return"progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"},toString:function(format){var formatSet=!!format;format=format||this._format;var formattedString=false;var hasAlpha=this._a<1&&this._a>=0;var needsAlphaFormat=!formatSet&&hasAlpha&&(format==="hex"||format==="hex6"||format==="hex3"||format==="name");if(needsAlphaFormat){if(format==="name"&&this._a===0){return this.toName()}return this.toRgbString()}if(format==="rgb"){formattedString=this.toRgbString()}if(format==="prgb"){formattedString=this.toPercentageRgbString()}if(format==="hex"||format==="hex6"){formattedString=this.toHexString()}if(format==="hex3"){formattedString=this.toHexString(true)}if(format==="hex8"){formattedString=this.toHex8String()}if(format==="name"){formattedString=this.toName()}if(format==="hsl"){formattedString=this.toHslString()}if(format==="hsv"){formattedString=this.toHsvString()}return formattedString||this.toHexString()},_applyModification:function(fn,args){var color=fn.apply(null,[this].concat([].slice.call(args)));this._r=color._r;this._g=color._g;this._b=color._b;this.setAlpha(color._a);return this},lighten:function(){return this._applyModification(lighten,arguments)},brighten:function(){return this._applyModification(brighten,arguments)},darken:function(){return this._applyModification(darken,arguments)},desaturate:function(){return this._applyModification(desaturate,arguments)},saturate:function(){return this._applyModification(saturate,arguments)},greyscale:function(){return this._applyModification(greyscale,arguments)},spin:function(){return this._applyModification(spin,arguments)},_applyCombination:function(fn,args){return fn.apply(null,[this].concat([].slice.call(args)))},analogous:function(){return this._applyCombination(analogous,arguments)},complement:function(){return this._applyCombination(complement,arguments)},monochromatic:function(){return this._applyCombination(monochromatic,arguments)},splitcomplement:function(){return this._applyCombination(splitcomplement,arguments)},triad:function(){return this._applyCombination(triad,arguments)},tetrad:function(){return this._applyCombination(tetrad,arguments)}};tinycolor.fromRatio=function(color,opts){if(typeof color=="object"){var newColor={};for(var i in color){if(color.hasOwnProperty(i)){if(i==="a"){newColor[i]=color[i]}else{newColor[i]=convertToPercentage(color[i])}}}color=newColor}return tinycolor(color,opts)};function inputToRGB(color){var rgb={r:0,g:0,b:0};var a=1;var ok=false;var format=false;if(typeof color=="string"){color=stringInputToObject(color)}if(typeof color=="object"){if(color.hasOwnProperty("r")&&color.hasOwnProperty("g")&&color.hasOwnProperty("b")){rgb=rgbToRgb(color.r,color.g,color.b);ok=true;format=String(color.r).substr(-1)==="%"?"prgb":"rgb"}else if(color.hasOwnProperty("h")&&color.hasOwnProperty("s")&&color.hasOwnProperty("v")){color.s=convertToPercentage(color.s);color.v=convertToPercentage(color.v);rgb=hsvToRgb(color.h,color.s,color.v);ok=true;format="hsv"}else if(color.hasOwnProperty("h")&&color.hasOwnProperty("s")&&color.hasOwnProperty("l")){color.s=convertToPercentage(color.s);color.l=convertToPercentage(color.l);rgb=hslToRgb(color.h,color.s,color.l);ok=true;format="hsl"}if(color.hasOwnProperty("a")){a=color.a}}a=boundAlpha(a);return{ok:ok,format:color.format||format,r:mathMin(255,mathMax(rgb.r,0)),g:mathMin(255,mathMax(rgb.g,0)),b:mathMin(255,mathMax(rgb.b,0)),a:a}}function rgbToRgb(r,g,b){return{r:bound01(r,255)*255,g:bound01(g,255)*255,b:bound01(b,255)*255}}function rgbToHsl(r,g,b){r=bound01(r,255);g=bound01(g,255);b=bound01(b,255);var max=mathMax(r,g,b),min=mathMin(r,g,b);var h,s,l=(max+min)/2;if(max==min){h=s=0}else{var d=max-min;s=l>.5?d/(2-max-min):d/(max+min);switch(max){case r:h=(g-b)/d+(g1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p}if(s===0){r=g=b=l}else{var q=l<.5?l*(1+s):l+s-l*s;var p=2*l-q;r=hue2rgb(p,q,h+1/3);g=hue2rgb(p,q,h);b=hue2rgb(p,q,h-1/3)}return{r:r*255,g:g*255,b:b*255}}function rgbToHsv(r,g,b){r=bound01(r,255);g=bound01(g,255);b=bound01(b,255);var max=mathMax(r,g,b),min=mathMin(r,g,b);var h,s,v=max;var d=max-min;s=max===0?0:d/max;if(max==min){h=0}else{switch(max){case r:h=(g-b)/d+(g>1)+720)%360;--results;){hsl.h=(hsl.h+part)%360;ret.push(tinycolor(hsl))}return ret}function monochromatic(color,results){results=results||6;var hsv=tinycolor(color).toHsv();var h=hsv.h,s=hsv.s,v=hsv.v;var ret=[];var modification=1/results;while(results--){ret.push(tinycolor({h:h,s:s,v:v}));v=(v+modification)%1}return ret}tinycolor.mix=function(color1,color2,amount){amount=amount===0?0:amount||50;var rgb1=tinycolor(color1).toRgb();var rgb2=tinycolor(color2).toRgb();var p=amount/100;var w=p*2-1;var a=rgb2.a-rgb1.a;var w1;if(w*a==-1){w1=w}else{w1=(w+a)/(1+w*a)}w1=(w1+1)/2;var w2=1-w1;var rgba={r:rgb2.r*w1+rgb1.r*w2,g:rgb2.g*w1+rgb1.g*w2,b:rgb2.b*w1+rgb1.b*w2,a:rgb2.a*p+rgb1.a*(1-p)};return tinycolor(rgba)};tinycolor.readability=function(color1,color2){var c1=tinycolor(color1);var c2=tinycolor(color2);var rgb1=c1.toRgb();var rgb2=c2.toRgb();var brightnessA=c1.getBrightness();var brightnessB=c2.getBrightness();var colorDiff=Math.max(rgb1.r,rgb2.r)-Math.min(rgb1.r,rgb2.r)+Math.max(rgb1.g,rgb2.g)-Math.min(rgb1.g,rgb2.g)+Math.max(rgb1.b,rgb2.b)-Math.min(rgb1.b,rgb2.b);return{brightness:Math.abs(brightnessA-brightnessB),color:colorDiff}};tinycolor.isReadable=function(color1,color2){var readability=tinycolor.readability(color1,color2);return readability.brightness>125&&readability.color>500};tinycolor.mostReadable=function(baseColor,colorList){var bestColor=null;var bestScore=0;var bestIsReadable=false;for(var i=0;i125&&readability.color>500;var score=3*(readability.brightness/125)+readability.color/500;if(readable&&!bestIsReadable||readable&&bestIsReadable&&score>bestScore||!readable&&!bestIsReadable&&score>bestScore){bestIsReadable=readable;bestScore=score;bestColor=tinycolor(colorList[i])}}return bestColor};var names=tinycolor.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"};var hexNames=tinycolor.hexNames=flip(names);function flip(o){var flipped={};for(var i in o){if(o.hasOwnProperty(i)){flipped[o[i]]=i}}return flipped}function boundAlpha(a){a=parseFloat(a);if(isNaN(a)||a<0||a>1){a=1}return a}function bound01(n,max){if(isOnePointZero(n)){n="100%"}var processPercent=isPercentage(n);n=mathMin(max,mathMax(0,parseFloat(n)));if(processPercent){n=parseInt(n*max,10)/100}if(math.abs(n-max)<1e-6){return 1}return n%max/parseFloat(max)}function clamp01(val){return mathMin(1,mathMax(0,val))}function parseIntFromHex(val){return parseInt(val,16)}function isOnePointZero(n){return typeof n=="string"&&n.indexOf(".")!=-1&&parseFloat(n)===1}function isPercentage(n){return typeof n==="string"&&n.indexOf("%")!=-1}function pad2(c){return c.length==1?"0"+c:""+c}function convertToPercentage(n){if(n<=1){n=n*100+"%"}return n}function convertDecimalToHex(d){return Math.round(parseFloat(d)*255).toString(16)}function convertHexToDecimal(h){return parseIntFromHex(h)/255}var matchers=function(){var CSS_INTEGER="[-\\+]?\\d+%?";var CSS_NUMBER="[-\\+]?\\d*\\.\\d+%?";var CSS_UNIT="(?:"+CSS_NUMBER+")|(?:"+CSS_INTEGER+")";var PERMISSIVE_MATCH3="[\\s|\\(]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")\\s*\\)?";var PERMISSIVE_MATCH4="[\\s|\\(]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")\\s*\\)?";return{rgb:new RegExp("rgb"+PERMISSIVE_MATCH3),rgba:new RegExp("rgba"+PERMISSIVE_MATCH4),hsl:new RegExp("hsl"+PERMISSIVE_MATCH3),hsla:new RegExp("hsla"+PERMISSIVE_MATCH4),hsv:new RegExp("hsv"+PERMISSIVE_MATCH3),hsva:new RegExp("hsva"+PERMISSIVE_MATCH4),hex3:/^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex8:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/}}();function stringInputToObject(color){color=color.replace(trimLeft,"").replace(trimRight,"").toLowerCase();var named=false;if(names[color]){color=names[color];named=true}else if(color=="transparent"){return{r:0,g:0,b:0,a:0,format:"name"}}var match;if(match=matchers.rgb.exec(color)){return{r:match[1],g:match[2],b:match[3]}}if(match=matchers.rgba.exec(color)){return{r:match[1],g:match[2],b:match[3],a:match[4]}}if(match=matchers.hsl.exec(color)){return{h:match[1],s:match[2],l:match[3]}}if(match=matchers.hsla.exec(color)){return{h:match[1],s:match[2],l:match[3],a:match[4]}}if(match=matchers.hsv.exec(color)){return{h:match[1],s:match[2],v:match[3]}}if(match=matchers.hsva.exec(color)){return{h:match[1],s:match[2],v:match[3],a:match[4]}}if(match=matchers.hex8.exec(color)){return{a:convertHexToDecimal(match[1]),r:parseIntFromHex(match[2]),g:parseIntFromHex(match[3]),b:parseIntFromHex(match[4]),format:named?"name":"hex8"}}if(match=matchers.hex6.exec(color)){return{r:parseIntFromHex(match[1]),g:parseIntFromHex(match[2]),b:parseIntFromHex(match[3]),format:named?"name":"hex"}}if(match=matchers.hex3.exec(color)){return{r:parseIntFromHex(match[1]+""+match[1]),g:parseIntFromHex(match[2]+""+match[2]),b:parseIntFromHex(match[3]+""+match[3]),format:named?"name":"hex"}}return false}window.tinycolor=tinycolor})();$(function(){if($.fn.spectrum.load){$.fn.spectrum.processNativeColorInputs()}})}); \ No newline at end of file diff --git a/script/bench.rb b/script/bench.rb index 27100005e6..9de35492a8 100644 --- a/script/bench.rb +++ b/script/bench.rb @@ -49,13 +49,15 @@ def run(command, opt = nil) system(command, out: $stdout, err: :out) end - exit unless exit_status + abort("Command '#{command}' failed with exit status #{$?}") unless exit_status end begin require 'facter' rescue LoadError run "gem install facter" + # Facter requires CFPropertyList, but doesn't install it. + run "gem install CFPropertyList" puts "please rerun script" exit end @@ -151,12 +153,12 @@ run("bundle exec ruby script/profile_db_generator.rb") puts "Getting api key" api_key = `bundle exec rake api_key:get`.split("\n")[-1] -def bench(path) +def bench(path, name) puts "Running apache bench warmup" add = "" add = "-c 3 " if @unicorn `ab #{add} -n 10 "http://127.0.0.1:#{@port}#{path}"` - puts "Benchmarking #{path}" + puts "Benchmarking #{name} @ #{path}" `ab -n #{@iterations} -e tmp/ab.csv "http://127.0.0.1:#{@port}#{path}"` percentiles = Hash[*[50, 75, 90, 99].zip([]).flatten] @@ -212,7 +214,7 @@ begin results = {} @best_of.times do tests.each do |name, url| - results[name] = best_of(bench(url),results[name]) + results[name] = best_of(bench(url, name),results[name]) end end diff --git a/script/bulk_import/base.rb b/script/bulk_import/base.rb new file mode 100644 index 0000000000..dde5995308 --- /dev/null +++ b/script/bulk_import/base.rb @@ -0,0 +1,525 @@ +require "pg" +require "set" +require "redcarpet" + +puts "Loading application..." +require_relative "../../config/environment" + +module BulkImport; end + +class BulkImport::Base + + NOW ||= "now()".freeze + PRIVATE_OFFSET ||= 2 ** 30 + + def initialize + db = ActiveRecord::Base.connection_config + @encoder = PG::TextEncoder::CopyRow.new + @raw_connection = PG.connect(dbname: db[:database], host: db[:host_names]&.first, port: db[:port]) + + @markdown = Redcarpet::Markdown.new( + Redcarpet::Render::HTML, + fenced_code_blocks: true, + autolink: true + ) + end + + def run + puts "Starting..." + preload_i18n + fix_highest_post_numbers + load_imported_ids + load_indexes + execute + fix_primary_keys + puts "Done!" + end + + def preload_i18n + puts "Preloading I18n..." + I18n.locale = ENV.fetch("LOCALE") { "en" }.to_sym + I18n.t("test") + ActiveSupport::Inflector.transliterate("test") + end + + def fix_highest_post_numbers + puts "Fixing highest post numbers..." + @raw_connection.exec <<-SQL + WITH X AS ( + SELECT topic_id + , COALESCE(MAX(post_number), 0) max_post_number + FROM posts + WHERE deleted_at IS NULL + GROUP BY topic_id + ) + UPDATE topics + SET highest_post_number = X.max_post_number + FROM X + WHERE id = X.topic_id + AND highest_post_number <> X.max_post_number + SQL + end + + def load_imported_ids + puts "Loading imported group ids..." + @groups = GroupCustomField.where(name: "import_id").pluck(:value, :group_id).to_h + @last_imported_group_id = @groups.keys.map(&:to_i).max || -1 + + puts "Loading imported user ids..." + @users = UserCustomField.where(name: "import_id").pluck(:value, :user_id).to_h + @last_imported_user_id = @users.keys.map(&:to_i).max || -1 + + puts "Loading imported category ids..." + @categories = CategoryCustomField.where(name: "import_id").pluck(:value, :category_id).to_h + @last_imported_category_id = @categories.keys.map(&:to_i).max || -1 + + puts "Loading imported topic ids..." + @topics = TopicCustomField.where(name: "import_id").pluck(:value, :topic_id).to_h + imported_topic_ids = @topics.keys.map(&:to_i) + @last_imported_topic_id = imported_topic_ids.select { |id| id < PRIVATE_OFFSET }.max || -1 + @last_imported_private_topic_id = imported_topic_ids.select { |id| id > PRIVATE_OFFSET }.max || (PRIVATE_OFFSET - 1) + + puts "Loading imported post ids..." + @posts = PostCustomField.where(name: "import_id").pluck(:value, :post_id).to_h + imported_post_ids = @posts.keys.map(&:to_i) + @last_imported_post_id = imported_post_ids.select { |id| id < PRIVATE_OFFSET }.max || -1 + @last_imported_private_post_id = imported_post_ids.select { |id| id > PRIVATE_OFFSET }.max || (PRIVATE_OFFSET - 1) + end + + def load_indexes + puts "Loading groups indexes..." + @last_group_id = Group.unscoped.maximum(:id) + @group_names = Group.unscoped.pluck(:name).map(&:downcase).to_set + + puts "Loading users indexes..." + @last_user_id = User.unscoped.maximum(:id) + @emails = User.unscoped.pluck(:email).to_set + @usernames_lower = User.unscoped.pluck(:username_lower).to_set + @mapped_usernames = UserCustomField.joins(:user).where(name: "import_username").pluck("user_custom_fields.value", "users.username").to_h + + puts "Loading categories indexes..." + @last_category_id = Category.unscoped.maximum(:id) + @category_names = Category.unscoped.pluck(:parent_category_id, :name).map { |pci, name| "#{pci}-#{name}" }.to_set + + puts "Loading topics indexes..." + @last_topic_id = Topic.unscoped.maximum(:id) + @highest_post_number_by_topic_id = Topic.unscoped.pluck(:id, :highest_post_number).to_h + + puts "Loading posts indexes..." + @last_post_id = Post.unscoped.maximum(:id) + @post_number_by_post_id = Post.unscoped.pluck(:id, :post_number).to_h + @topic_id_by_post_id = Post.unscoped.pluck(:id, :topic_id).to_h + end + + def execute + raise NotImplementedError + end + + def fix_primary_keys + puts "Updating primary key sequences..." + @raw_connection.exec("SELECT setval('#{Group.sequence_name}', #{@last_group_id})") + @raw_connection.exec("SELECT setval('#{User.sequence_name}', #{@last_user_id})") + @raw_connection.exec("SELECT setval('#{Category.sequence_name}', #{@last_category_id})") + @raw_connection.exec("SELECT setval('#{Topic.sequence_name}', #{@last_topic_id})") + @raw_connection.exec("SELECT setval('#{Post.sequence_name}', #{@last_post_id})") + end + + def group_id_from_imported_id(id); @groups[id.to_s]; end + def user_id_from_imported_id(id); @users[id.to_s]; end + def category_id_from_imported_id(id); @categories[id.to_s]; end + def topic_id_from_imported_id(id); @topics[id.to_s]; end + def post_id_from_imported_id(id); @posts[id.to_s]; end + + def post_number_from_imported_id(id); @post_number_by_post_id[post_id_from_imported_id(id)]; end + def topic_id_from_imported_post_id(id); @topic_id_by_post_id[post_id_from_imported_id(id)]; end + + GROUP_COLUMNS ||= %i{ + id name title bio_raw bio_cooked created_at updated_at + } + + USER_COLUMNS ||= %i{ + id username username_lower name email active trust_level admin moderator + date_of_birth ip_address registration_ip_address primary_group_id + suspended_at suspended_till last_emailed_at created_at updated_at + } + + USER_PROFILE_COLUMNS ||= %i{ + user_id location website bio_raw bio_cooked views + } + + GROUP_USER_COLUMNS ||= %i{ + group_id user_id created_at updated_at + } + + CATEGORY_COLUMNS ||= %i{ + id name name_lower slug user_id description position parent_category_id + created_at updated_at + } + + TOPIC_COLUMNS ||= %i{ + id archetype title fancy_title slug user_id last_post_user_id category_id + visible closed pinned_at views created_at bumped_at updated_at + } + + POST_COLUMNS ||= %i{ + id user_id last_editor_id topic_id post_number sort_order reply_to_post_number + raw cooked hidden word_count created_at last_version_at updated_at + } + + TOPIC_ALLOWED_USER_COLUMNS ||= %i{ + topic_id user_id created_at updated_at + } + + def create_groups(rows, &block); create_records(rows, "group", GROUP_COLUMNS, &block); end + + def create_users(rows, &block) + @imported_usernames = {} + + create_records(rows, "user", USER_COLUMNS, &block) + + create_custom_fields("user", "username", @imported_usernames.keys) do |username| + { + record_id: @imported_usernames[username], + value: username, + } + end + end + + def create_user_profiles(rows, &block); create_records(rows, "user_profile", USER_PROFILE_COLUMNS, &block); end + def create_group_users(rows, &block); create_records(rows, "group_user", GROUP_USER_COLUMNS, &block); end + def create_categories(rows, &block); create_records(rows, "category", CATEGORY_COLUMNS, &block); end + def create_topics(rows, &block); create_records(rows, "topic", TOPIC_COLUMNS, &block); end + def create_posts(rows, &block); create_records(rows, "post", POST_COLUMNS, &block); end + def create_topic_allowed_users(rows, &block); create_records(rows, "topic_allowed_user", TOPIC_ALLOWED_USER_COLUMNS, &block); end + + def process_group(group) + @groups[group[:imported_id].to_s] = group[:id] = @last_group_id += 1 + + group[:name] = fix_name(group[:name]) + + unless @group_names.add?(group[:name].downcase) + group_name = group[:name] + "_1" + group_name.next! until @group_names.add?(group_name.downcase) + group[:name] = group_name + end + + group[:title] = group[:title].scrub.strip.presence + group[:bio_raw] = group[:bio_raw].scrub.strip.presence + group[:bio_cooked] = pre_cook(group[:bio_raw]) if group[:bio_raw].present? + group[:created_at] ||= NOW + group[:updated_at] ||= group[:created_at] + group + end + + def process_user(user) + @users[user[:imported_id].to_s] = user[:id] = @last_user_id += 1 + + imported_username = user[:username].dup + + user[:username] = fix_name(user[:username]).presence || random_username + + if user[:username] != imported_username + @imported_usernames[imported_username] = user[:id] + @mapped_usernames[imported_username] = user[:username] + end + + # unique username_lower + unless @usernames_lower.add?(user[:username].downcase) + username = user[:username] + "_1" + username.next! until @usernames_lower.add?(username.downcase) + user[:username] = username + end + + user[:username_lower] = user[:username].downcase + user[:email] ||= random_email + user[:email].downcase! + + # unique email + user[:email] = random_email until user[:email] =~ EmailValidator.email_regex && @emails.add?(user[:email]) + user[:trust_level] ||= TrustLevel[1] + user[:active] = true unless user.has_key?(:active) + user[:admin] ||= false + user[:moderator] ||= false + user[:last_emailed_at] ||= NOW + user[:created_at] ||= NOW + user[:updated_at] ||= user[:created_at] + user + end + + def process_user_profile(user_profile) + user_profile[:bio_raw] = (user_profile[:bio_raw].presence || "").scrub.strip.presence + user_profile[:bio_cooked] = pre_cook(user_profile[:bio_raw]) if user_profile[:bio_raw].present? + user_profile + end + + def process_group_user(group_user) + group_user[:created_at] = NOW + group_user[:updated_at] = NOW + group_user + end + + def process_category(category) + @categories[category[:imported_id].to_s] = category[:id] = @last_category_id += 1 + category[:name] = category[:name][0...50].scrub.strip + # TODO: unique name + category[:name_lower] = category[:name].downcase + category[:slug] ||= Slug.ascii_generator(category[:name_lower]) + category[:description] = (category[:description] || "").scrub.strip.presence + category[:user_id] ||= Discourse::SYSTEM_USER_ID + category[:created_at] ||= NOW + category[:updated_at] ||= category[:created_at] + category + end + + def process_topic(topic) + @topics[topic[:imported_id].to_s] = topic[:id] = @last_topic_id += 1 + topic[:archetype] ||= Archetype.default + topic[:title] = topic[:title][0...255].scrub.strip + topic[:fancy_title] ||= pre_fancy(topic[:title]) + topic[:slug] ||= Slug.ascii_generator(topic[:title]) + topic[:user_id] ||= Discourse::SYSTEM_USER_ID + topic[:last_post_user_id] ||= topic[:user_id] + topic[:category_id] ||= -1 if topic[:archetype] != Archetype.private_message + topic[:visible] = true unless topic.has_key?(:visible) + topic[:closed] ||= false + topic[:views] ||= 0 + topic[:created_at] ||= NOW + topic[:bumped_at] ||= topic[:created_at] + topic[:updated_at] ||= topic[:created_at] + topic + end + + def process_post(post) + @posts[post[:imported_id].to_s] = post[:id] = @last_post_id += 1 + post[:user_id] ||= Discourse::SYSTEM_USER_ID + post[:last_editor_id] = post[:user_id] + @highest_post_number_by_topic_id[post[:topic_id]] ||= 0 + post[:post_number] = @highest_post_number_by_topic_id[post[:topic_id]] += 1 + post[:sort_order] = post[:post_number] + @post_number_by_post_id[post[:id]] = post[:post_number] + @topic_id_by_post_id[post[:id]] = post[:topic_id] + post[:raw] = (post[:raw] || "").scrub.strip.presence || "" + post[:raw] = process_raw post[:raw] + post[:cooked] = pre_cook post[:raw] + post[:hidden] ||= false + post[:word_count] = post[:raw].scan(/[[:word:]]+/).size + post[:created_at] ||= NOW + post[:last_version_at] = post[:created_at] + post[:updated_at] ||= post[:created_at] + post + end + + def process_topic_allowed_user(topic_allowed_user) + topic_allowed_user[:created_at] = NOW + topic_allowed_user[:updated_at] = NOW + topic_allowed_user + end + + def process_raw(raw) + # fix whitespaces + raw.gsub!(/(\\r)?\\n/, "\n") + raw.gsub!("\\t", "\t") + + # [HTML]...[/HTML] + raw.gsub!(/\[HTML\]/i, "\n\n```html\n") + raw.gsub!(/\[\/HTML\]/i, "\n```\n\n") + + # [PHP]...[/PHP] + raw.gsub!(/\[PHP\]/i, "\n\n```php\n") + raw.gsub!(/\[\/PHP\]/i, "\n```\n\n") + + # [HIGHLIGHT="..."] + raw.gsub!(/\[HIGHLIGHT="?(\w+)"?\]/i) { "\n\n```#{$1.downcase}\n" } + + # [CODE]...[/CODE] + # [HIGHLIGHT]...[/HIGHLIGHT] + raw.gsub!(/\[\/?CODE\]/i, "\n\n```\n\n") + raw.gsub!(/\[\/?HIGHLIGHT\]/i, "\n\n```\n\n") + + # [SAMP]...[/SAMP] + raw.gsub!(/\[\/?SAMP\]/i, "`") + + # replace all chevrons with HTML entities + # /!\ must be done /!\ + # - AFTER the "code" processing + # - BEFORE the "quote" processing + raw.gsub!(/`([^`]+?)`/im) { "`" + $1.gsub("<", "\u2603") + "`" } + raw.gsub!("<", "<") + raw.gsub!("\u2603", "<") + + raw.gsub!(/`([^`]+?)`/im) { "`" + $1.gsub(">", "\u2603") + "`" } + raw.gsub!(">", ">") + raw.gsub!("\u2603", ">") + + raw.gsub!(/\[\/?I\]/i, "*") + raw.gsub!(/\[\/?B\]/i, "**") + raw.gsub!(/\[\/?U\]/i, "") + + raw.gsub!(/\[\/?RED\]/i, "") + raw.gsub!(/\[\/?BLUE\]/i, "") + + raw.gsub!(/\[AUTEUR\].+?\[\/AUTEUR\]/im, "") + raw.gsub!(/\[VOIRMSG\].+?\[\/VOIRMSG\]/im, "") + raw.gsub!(/\[PSEUDOID\].+?\[\/PSEUDOID\]/im, "") + + # [IMG]...[/IMG] + raw.gsub!(/(?:\s*\[IMG\]\s*)+(.+?)(?:\s*\[\/IMG\]\s*)+/im) { "\n\n#{$1}\n\n" } + + # [URL=...]...[/URL] + raw.gsub!(/\[URL="?(.+?)"?\](.+?)\[\/URL\]/im) { "[#{$2.strip}](#{$1})" } + + # [URL]...[/URL] + # [MP3]...[/MP3] + raw.gsub!(/\[\/?URL\]/i, "") + raw.gsub!(/\[\/?MP3\]/i, "") + + # [FONT=blah] and [COLOR=blah] + raw.gsub!(/\[FONT=.*?\](.*?)\[\/FONT\]/im, "\1") + raw.gsub!(/\[COLOR=.*?\](.*?)\[\/COLOR\]/im, "\1") + + raw.gsub!(/\[SIZE=.*?\](.*?)\[\/SIZE\]/im, "\1") + raw.gsub!(/\[H=.*?\](.*?)\[\/H\]/im, "\1") + + # [CENTER]...[/CENTER] + raw.gsub!(/\[CENTER\](.*?)\[\/CENTER\]/im, "\1") + + # [INDENT]...[/INDENT] + raw.gsub!(/\[INDENT\](.*?)\[\/INDENT\]/im, "\1") + raw.gsub!(/\[TABLE\](.*?)\[\/TABLE\]/im, "\1") + raw.gsub!(/\[TR\](.*?)\[\/TR\]/im, "\1") + raw.gsub!(/\[TD\](.*?)\[\/TD\]/im, "\1") + raw.gsub!(/\[TD="?.*?"?\](.*?)\[\/TD\]/im, "\1") + + # [QUOTE]...[/QUOTE] + raw.gsub!(/\[QUOTE\](.+?)\[\/QUOTE\]/im) { |quote| + quote.gsub!(/\[QUOTE\](.+?)\[\/QUOTE\]/im) { "\n#{$1}\n" } + quote.gsub!(/\n(.+?)/) { "\n> #{$1}" } + } + + # [QUOTE=;]...[/QUOTE] + raw.gsub!(/\[QUOTE=([^;]+);(\d+)\](.+?)\[\/QUOTE\]/im) do + imported_username, imported_postid, quote = $1, $2, $3 + + username = @mapped_usernames[imported_username] || imported_username + post_id = post_id_from_imported_id(imported_postid) + post_number = @post_number_by_post_id[post_id] + topic_id = @topic_id_by_post_id[post_id] + + if post_number && topic_id + "\n[quote=\"#{username}, post:#{post_number}, topic:#{topic_id}\"]\n#{quote}\n[/quote]" + else + "\n[quote=\"#{username}\"]\n#{quote}\n[/quote]\n" + end + end + + # [YOUTUBE][/YOUTUBE] + raw.gsub!(/\[YOUTUBE\](.+?)\[\/YOUTUBE\]/i) { "\nhttps://www.youtube.com/watch?v=#{$1}\n" } + raw.gsub!(/\[DAILYMOTION\](.+?)\[\/DAILYMOTION\]/i) { "\nhttps://www.dailymotion.com/video/#{$1}\n" } + + # [VIDEO=youtube;]...[/VIDEO] + raw.gsub!(/\[VIDEO=YOUTUBE;([^\]]+)\].*?\[\/VIDEO\]/i) { "\nhttps://www.youtube.com/watch?v=#{$1}\n" } + raw.gsub!(/\[VIDEO=DAILYMOTION;([^\]]+)\].*?\[\/VIDEO\]/i) { "\nhttps://www.dailymotion.com/video/#{$1}\n" } + + # [SPOILER=Some hidden stuff]SPOILER HERE!![/SPOILER] + raw.gsub!(/\[SPOILER="?(.+?)"?\](.+?)\[\/SPOILER\]/im) { "\n#{$1}\n[spoiler]#{$2}[/spoiler]\n" } + + raw + end + + def create_records(rows, name, columns) + start = Time.now + + imported_ids = [] + process_method_name = "process_#{name}" + sql = "COPY #{name.pluralize} (#{columns.join(",")}) FROM STDIN" + + @raw_connection.copy_data(sql, @encoder) do + rows.each do |row| + mapped = yield(row) + next unless mapped + processed = send(process_method_name, mapped) + imported_ids << mapped[:imported_id] + @raw_connection.put_copy_data columns.map { |c| processed[c] } + print "\r%7d - %6d/sec".freeze % [imported_ids.size, imported_ids.size.to_f / (Time.now - start)] if imported_ids.size % 5000 == 0 + end + end + + if imported_ids.size > 0 + print "\r%7d - %6d/sec".freeze % [imported_ids.size, imported_ids.size.to_f / (Time.now - start)] + puts + end + + id_mapping_method_name = "#{name}_id_from_imported_id".freeze + return unless respond_to?(id_mapping_method_name) + create_custom_fields(name, "id", imported_ids) do |imported_id| + { + record_id: send(id_mapping_method_name, imported_id), + value: imported_id, + } + end + end + + def create_custom_fields(table, name, rows) + name = "import_#{name}" + sql = "COPY #{table}_custom_fields (#{table}_id, name, value, created_at, updated_at) FROM STDIN" + @raw_connection.copy_data(sql, @encoder) do + rows.each do |row| + cf = yield row + next unless cf + @raw_connection.put_copy_data [cf[:record_id], name, cf[:value], NOW, NOW] + end + end + end + + def fix_name(name) + return if name.blank? + name.scrub! + name = ActiveSupport::Inflector.transliterate(name) + name.gsub!(/[^\w.-]+/, "_") + name.gsub!(/^\W+/, "") + name.gsub!(/[^A-Za-z0-9]+$/, "") + name.gsub!(/([-_.]{2,})/) { $1.first } + name.strip! + name + end + + def random_username + "Anonymous_#{SecureRandom.hex}" + end + + def random_email + "#{SecureRandom.hex}@ema.il" + end + + def pre_cook(raw) + cooked = @markdown.render(raw).scrub.strip + + cooked.gsub!(/\[QUOTE="?([^,"]+)(?:, post:(\d+), topic:(\d+))?"?\](.+?)\[\/QUOTE\]/im) do + username, post_id, topic_id = $1, $2, $3 + quote = @markdown.render($4.presence || "").scrub.strip + + if post_id.present? && topic_id.present? + <<-HTML + + HTML + else + <<-HTML + + HTML + end + end + + cooked.scrub.strip + end + + def pre_fancy(title) + Redcarpet::Render::SmartyPants.render(ERB::Util.html_escape(title)).scrub.strip + end + +end diff --git a/script/bulk_import/vbulletin.rb b/script/bulk_import/vbulletin.rb new file mode 100644 index 0000000000..2121a47b2d --- /dev/null +++ b/script/bulk_import/vbulletin.rb @@ -0,0 +1,377 @@ +require_relative "base" +require "mysql2" +require "htmlentities" + +class BulkImport::VBulletin < BulkImport::Base + + SUSPENDED_TILL ||= Date.new(3000, 1, 1) + + def initialize + super + + host = ENV["DB_HOST"] + username = ENV["DB_USERNAME"] || "root" + password = ENV["DB_PASSWORD"] + database = ENV["DB_NAME"] || "vbulletin" + + @html_entities = HTMLEntities.new + + @client = Mysql2::Client.new(host: host, username: username, password: password, database: database) + @client.query_options.merge!(as: :array, cache_rows: false) + end + + def execute + import_groups + import_users + import_group_users + + import_user_passwords + import_user_salts + import_user_profiles + + import_categories + import_topics + import_posts + + import_private_topics + import_topic_allowed_users + import_private_posts + end + + def import_groups + puts "Importing groups..." + + groups = mysql_stream <<-SQL + SELECT usergroupid, title, description, usertitle + FROM usergroup + WHERE usergroupid > #{@last_imported_group_id} + ORDER BY usergroupid + SQL + + create_groups(groups) do |row| + { + imported_id: row[0], + name: html_decode(row[1]), + bio_raw: html_decode(row[2]), + title: html_decode(row[3]), + } + end + end + + def import_users + puts "Importing users..." + + users = mysql_stream <<-SQL + SELECT user.userid, username, email, joindate, birthday, ipaddress, user.usergroupid, bandate, liftdate + FROM user + LEFT JOIN userban ON userban.userid = user.userid + WHERE user.userid > #{@last_imported_user_id} + ORDER BY user.userid + SQL + + create_users(users) do |row| + u = { + imported_id: row[0], + username: row[1], + email: row[2], + created_at: Time.zone.at(row[3]), + date_of_birth: parse_birthday(row[4]), + primary_group_id: group_id_from_imported_id(row[6]), + } + u[:ip_address] = row[5][/\b(?:\d{1,3}\.){3}\d{1,3}\b/] if row[5].present? + if row[7] + u[:suspended_at] = Time.zone.at(row[7]) + u[:suspended_till] = row[8] > 0 ? Time.zone.at(row[8]) : SUSPENDED_TILL + end + u + end + end + + def import_group_users + puts "Importing group users..." + + group_users = mysql_stream <<-SQL + SELECT usergroupid, userid + FROM user + WHERE userid > #{@last_imported_user_id} + SQL + + create_group_users(group_users) do |row| + { + group_id: group_id_from_imported_id(row[0]), + user_id: user_id_from_imported_id(row[1]), + } + end + end + + def import_user_passwords + puts "Importing user passwords..." + + user_passwords = mysql_stream <<-SQL + SELECT userid, password + FROM user + WHERE userid > #{@last_imported_user_id} + ORDER BY userid + SQL + + create_custom_fields("user", "password", user_passwords) do |row| + { + record_id: user_id_from_imported_id(row[0]), + value: row[1], + } + end + end + + def import_user_salts + puts "Importing user salts..." + + user_salts = mysql_stream <<-SQL + SELECT userid, salt + FROM user + WHERE userid > #{@last_imported_user_id} + AND LENGTH(COALESCE(salt, '')) > 0 + ORDER BY userid + SQL + + create_custom_fields("user", "salt", user_salts) do |row| + { + record_id: user_id_from_imported_id(row[0]), + value: row[1], + } + end + end + + def import_user_profiles + puts "Importing user profiles..." + + user_profiles = mysql_stream <<-SQL + SELECT userid, homepage, profilevisits + FROM user + WHERE userid > #{@last_imported_user_id} + ORDER BY userid + SQL + + create_user_profiles(user_profiles) do |row| + { + user_id: user_id_from_imported_id(row[0]), + website: (URI.parse(row[1]).to_s rescue nil), + views: row[2], + } + end + end + + def import_categories + puts "Importing categories..." + + categories = mysql_query(<<-SQL + SELECT forumid, parentid, title, description, displayorder + FROM forum + WHERE forumid > #{@last_imported_category_id} + ORDER BY forumid + SQL + ).to_a + + return if categories.empty? + + parent_categories = categories.select { |c| c[1] == -1 } + children_categories = categories.select { |c| c[1] != -1 } + + parent_category_ids = Set.new parent_categories.map { |c| c[0] } + + # cut down the tree to only 2 levels of categories + children_categories.each do |cc| + until parent_category_ids.include?(cc[1]) + cc[1] = categories.find { |c| c[0] == cc[1] }[1] + end + end + + puts "Importing parent categories..." + create_categories(parent_categories) do |row| + { + imported_id: row[0], + name: html_decode(row[2]), + description: html_decode(row[3]), + position: row[4], + } + end + + puts "Importing children categories..." + create_categories(children_categories) do |row| + { + imported_id: row[0], + name: html_decode(row[2]), + description: html_decode(row[3]), + position: row[4], + parent_category_id: category_id_from_imported_id(row[1]), + } + end + end + + def import_topics + puts "Importing topics..." + + topics = mysql_stream <<-SQL + SELECT threadid, title, forumid, postuserid, open, dateline, views, visible, sticky + FROM thread + WHERE threadid > #{@last_imported_topic_id} + AND EXISTS (SELECT 1 FROM post WHERE post.threadid = thread.threadid) + ORDER BY threadid + SQL + + create_topics(topics) do |row| + created_at = Time.zone.at(row[5]) + + t = { + imported_id: row[0], + title: html_decode(row[1]), + category_id: category_id_from_imported_id(row[2]), + user_id: user_id_from_imported_id(row[3]), + closed: row[4] == 0, + created_at: created_at, + views: row[6], + visible: row[7] == 1, + } + + t[:pinned_at] = created_at if row[8] == 1 + + t + end + end + + def import_posts + puts "Importing posts..." + + posts = mysql_stream <<-SQL + SELECT postid, post.threadid, parentid, userid, post.dateline, post.visible, pagetext + FROM post + JOIN thread ON thread.threadid = post.threadid + WHERE postid > #{@last_imported_post_id} + ORDER BY postid + SQL + + create_posts(posts) do |row| + topic_id = topic_id_from_imported_id(row[1]) + replied_post_topic_id = topic_id_from_imported_post_id(row[2]) + reply_to_post_number = topic_id == replied_post_topic_id ? post_number_from_imported_id(row[2]) : nil + + { + imported_id: row[0], + topic_id: topic_id, + reply_to_post_number: reply_to_post_number, + user_id: user_id_from_imported_id(row[3]), + created_at: Time.zone.at(row[4]), + hidden: row[5] == 0, + raw: html_decode(row[6]), + } + end + end + + def import_private_topics + puts "Importing private topics..." + + @imported_topics = {} + + topics = mysql_stream <<-SQL + SELECT pmtextid, title, fromuserid, touserarray, dateline + FROM pmtext + WHERE pmtextid > (#{@last_imported_private_topic_id - PRIVATE_OFFSET}) + ORDER BY pmtextid + SQL + + create_topics(topics) do |row| + title = extract_pm_title(row[1]) + user_ids = [row[2], row[3].scan(/i:(\d+)/)].flatten.map(&:to_i).sort + key = [title, user_ids] + + next if @imported_topics.has_key?(key) + @imported_topics[key] = row[0] + PRIVATE_OFFSET + + { + archetype: Archetype.private_message, + imported_id: row[0] + PRIVATE_OFFSET, + title: title, + user_id: user_id_from_imported_id(row[2]), + created_at: Time.zone.at(row[4]), + } + end + end + + def import_topic_allowed_users + puts "Importing topic allowed users..." + + allowed_users = [] + + mysql_stream(<<-SQL + SELECT pmtextid, touserarray + FROM pmtext + WHERE pmtextid > (#{@last_imported_private_topic_id - PRIVATE_OFFSET}) + ORDER BY pmtextid + SQL + ).each do |row| + next unless topic_id = topic_id_from_imported_id(row[0] + PRIVATE_OFFSET) + row[1].scan(/i:(\d+)/).flatten.each do |id| + next unless user_id = user_id_from_imported_id(id) + allowed_users << [topic_id, user_id] + end + end + + create_topic_allowed_users(allowed_users) do |row| + { + topic_id: row[0], + user_id: row[1], + } + end + end + + def import_private_posts + puts "Importing private posts..." + + posts = mysql_stream <<-SQL + SELECT pmtextid, title, fromuserid, touserarray, dateline, message + FROM pmtext + WHERE pmtextid > #{@last_imported_private_post_id - PRIVATE_OFFSET} + ORDER BY pmtextid + SQL + + create_posts(posts) do |row| + title = extract_pm_title(row[1]) + user_ids = [row[2], row[3].scan(/i:(\d+)/)].flatten.map(&:to_i).sort + key = [title, user_ids] + + next unless topic_id = topic_id_from_imported_id(@imported_topics[key]) + + { + imported_id: row[0] + PRIVATE_OFFSET, + topic_id: topic_id, + user_id: user_id_from_imported_id(row[2]), + created_at: Time.zone.at(row[4]), + raw: html_decode(row[5]), + } + end + end + + def extract_pm_title(title) + html_decode(title).scrub.gsub(/^Re\s*:\s*/i, "") + end + + def html_decode(text) + @html_entities.decode((text.presence || "").scrub) + end + + def parse_birthday(birthday) + return if birthday.blank? + date_of_birth = Date.strptime(birthday, "%m-%d-%Y") + date_of_birth.year < 1904 ? Date.new(1904, date_of_birth.month, date_of_birth.day) : date_of_birth + end + + def mysql_stream(sql) + @client.query(sql, stream: true) + end + + def mysql_query(sql) + @client.query(sql) + end + +end + +BulkImport::VBulletin.new.run diff --git a/script/import_scripts/base.rb b/script/import_scripts/base.rb index 4d001e4a8c..616a534a5d 100644 --- a/script/import_scripts/base.rb +++ b/script/import_scripts/base.rb @@ -106,32 +106,16 @@ class ImportScripts::Base raise NotImplementedError end - def post_id_from_imported_post_id(import_id) - @lookup.post_id_from_imported_post_id(import_id) - end - - def topic_lookup_from_imported_post_id(import_id) - @lookup.topic_lookup_from_imported_post_id(import_id) - end - - def group_id_from_imported_group_id(import_id) - @lookup.group_id_from_imported_group_id(import_id) - end - - def find_group_by_import_id(import_id) - @lookup.find_group_by_import_id(import_id) - end - - def user_id_from_imported_user_id(import_id) - @lookup.user_id_from_imported_user_id(import_id) - end - - def find_user_by_import_id(import_id) - @lookup.find_user_by_import_id(import_id) - end - - def category_id_from_imported_category_id(import_id) - @lookup.category_id_from_imported_category_id(import_id) + %i{ post_id_from_imported_post_id + topic_lookup_from_imported_post_id + group_id_from_imported_group_id + find_group_by_import_id + user_id_from_imported_user_id + find_user_by_import_id + category_id_from_imported_category_id + add_group add_user add_category add_topic add_post + }.each do |method_name| + delegate method_name, to: :@lookup end def create_admin(opts={}) @@ -165,14 +149,14 @@ class ImportScripts::Base results.each do |result| g = yield(result) - if @lookup.group_id_from_imported_group_id(g[:id]) + if group_id_from_imported_group_id(g[:id]) skipped += 1 else new_group = create_group(g, g[:id]) created_group(new_group) if new_group.valid? - @lookup.add_group(g[:id].to_s, new_group) + add_group(g[:id].to_s, new_group) created += 1 else failed += 1 @@ -247,14 +231,14 @@ class ImportScripts::Base else import_id = u[:id] - if @lookup.user_id_from_imported_user_id(import_id) + if user_id_from_imported_user_id(import_id) skipped += 1 elsif u[:email].present? new_user = create_user(u, import_id) created_user(new_user) if new_user && new_user.valid? && new_user.user_profile && new_user.user_profile.valid? - @lookup.add_user(import_id.to_s, new_user) + add_user(import_id.to_s, new_user) created += 1 else failed += 1 @@ -375,7 +359,7 @@ class ImportScripts::Base params = yield(c) # block returns nil to skip - if params.nil? || @lookup.category_id_from_imported_category_id(params[:id]) + if params.nil? || category_id_from_imported_category_id(params[:id]) skipped += 1 else # Basic massaging on the category name @@ -422,7 +406,7 @@ class ImportScripts::Base new_category.custom_fields["import_id"] = import_id if import_id new_category.save! - @lookup.add_category(import_id, new_category) + add_category(import_id, new_category) post_create_action.try(:call, new_category) @@ -453,14 +437,14 @@ class ImportScripts::Base else import_id = params.delete(:id).to_s - if @lookup.post_id_from_imported_post_id(import_id) + if post_id_from_imported_post_id(import_id) skipped += 1 # already imported this post else begin new_post = create_post(params, import_id) if new_post.is_a?(Post) - @lookup.add_post(import_id, new_post) - @lookup.add_topic(new_post) + add_post(import_id, new_post) + add_topic(new_post) created_post(new_post) @@ -533,8 +517,8 @@ class ImportScripts::Base if params.nil? skipped += 1 else - user.id = @lookup.user_id_from_imported_user_id(params[:user_id]) - post.id = @lookup.post_id_from_imported_post_id(params[:post_id]) + user.id = user_id_from_imported_user_id(params[:user_id]) + post.id = post_id_from_imported_post_id(params[:post_id]) if user.id.nil? || post.id.nil? skipped += 1 diff --git a/script/import_scripts/jive_api.rb b/script/import_scripts/jive_api.rb new file mode 100644 index 0000000000..9b5ff314b7 --- /dev/null +++ b/script/import_scripts/jive_api.rb @@ -0,0 +1,211 @@ +require "nokogiri" +require "htmlentities" +require_relative "./../../lib/html_to_markdown.rb" +require File.expand_path(File.dirname(__FILE__) + "/base.rb") + +class ImportScripts::JiveApi < ImportScripts::Base + + USER_COUNT ||= 1000 + POST_COUNT ||= 100 + STAFF_GUARDIAN ||= Guardian.new(Discourse.system_user) + + def initialize + super + @base_uri = ENV["BASE_URI"] + @username = ENV["USERNAME"] + @password = ENV["PASSWORD"] + @htmlentities = HTMLEntities.new + end + + def execute + import_users + import_discussions + import_posts + + mark_topics_as_solved + end + + def import_users + puts "", "importing users..." + + imported_users = 0 + start_index = [0, UserCustomField.where(name: "import_id").count - USER_COUNT].max + + loop do + users = get("people/@all?fields=initialLogin,emails,displayName,mentionName,thumbnailUrl,-resources&count=#{USER_COUNT}&startIndex=#{start_index}", true) + create_users(users["list"], offset: imported_users) do |user| + { + id: user["id"], + created_at: user["initialLogin"], + email: user["emails"].find { |email| email["primary"] }["value"], + username: user["mentionName"], + name: user["displayName"], + avatar_url: user["thumbnailUrl"], + } + end + + break if users["list"].size < USER_COUNT || users["links"].blank? || users["links"]["next"].blank? + imported_users += users["list"].size + break unless start_index = users["links"]["next"][/startIndex=(\d+)/, 1] + end + end + + def import_discussions + puts "", "importing discussions & questions..." + + start_index = 0 + fields = "fields=published,contentID,author.id,content.text,subject,viewCount,question,-resources,-author.resources" + filter = "&filter=creationDate(null,2017-01-01T00:00:00Z)" + + loop do + discussions = get("contents?#{fields}&filter=status(published)&filter=type(discussion)#{filter}&sort=dateCreatedAsc&count=#{POST_COUNT}&startIndex=#{start_index}") + discussions["list"].each do |discussion| + topic = { + id: discussion["contentID"], + created_at: discussion["published"], + title: @htmlentities.decode(discussion["subject"]), + raw: process_raw(discussion["content"]["text"]), + user_id: user_id_from_imported_user_id(discussion["author"]["id"]) || Discourse::SYSTEM_USER_ID, + # category: discussion["question"] ? 5 : 21, + views: discussion["viewCount"], + custom_fields: { import_id: discussion["contentID"] }, + } + + post_id = post_id_from_imported_post_id(topic[:id]) + parent_post = post_id ? Post.unscoped.find_by(id: post_id) : create_post(topic, topic[:id]) + + import_comments(discussion["contentID"], parent_post.topic_id) if parent_post + end + + break if discussions["list"].size < POST_COUNT || discussions["links"].blank? || discussions["links"]["next"].blank? + break unless start_index = discussions["links"]["next"][/startIndex=(\d+)/, 1] + end + end + + def import_comments(discussion_id, topic_id) + start_index = 0 + fields = "fields=published,author.id,content.text,parent,answer,-resources,-author.resources" + + loop do + comments = get("messages/contents/#{discussion_id}?#{fields}&hierarchical=false&count=#{POST_COUNT}&startIndex=#{start_index}") + comments["list"].each do |comment| + next if post_id_from_imported_post_id(comment["id"]) + + post = { + id: comment["id"], + created_at: comment["published"], + topic_id: topic_id, + user_id: user_id_from_imported_user_id(comment["author"]["id"]) || Discourse::SYSTEM_USER_ID, + raw: process_raw(comment["content"]["text"]), + custom_fields: { import_id: comment["id"] }, + } + post[:custom_fields][:is_accepted_answer] = true if comment["answer"] + + if parent_post_id = comment["parent"][/\/messages\/(\d+)/, 1] + if parent = topic_lookup_from_imported_post_id(parent_post_id) + post[:reply_to_post_number] = parent[:post_number] + end + end + + create_post(post, post[:id]) + end + + break if comments["list"].size < POST_COUNT || comments["links"].blank? || comments["links"]["next"].blank? + break unless start_index = comments["links"]["next"][/startIndex=(\d+)/, 1] + end + end + + def import_posts + puts "", "importing blog posts..." + + start_index = 0 + fields = "fields=published,contentID,author.id,content.text,subject,viewCount,permalink,-resources,-author.resources" + filter = "&filter=creationDate(null,2016-05-01T00:00:00Z)" + + loop do + posts = get("contents?#{fields}&filter=status(published)&filter=type(post)#{filter}&sort=dateCreatedAsc&count=#{POST_COUNT}&startIndex=#{start_index}") + posts["list"].each do |post| + next if post_id_from_imported_post_id(post["contentID"]) + pp = { + id: post["contentID"], + created_at: post["published"], + title: @htmlentities.decode(post["subject"]), + raw: process_raw(post["content"]["text"]), + user_id: user_id_from_imported_user_id(post["author"]["id"]) || Discourse::SYSTEM_USER_ID, + category: 7, + views: post["viewCount"], + custom_fields: { import_id: post["contentID"], import_permalink: post["permalink"] }, + } + + create_post(pp, pp[:id]) + end + + break if posts["list"].size < POST_COUNT || posts["links"].blank? || posts["links"]["next"].blank? + break unless start_index = posts["links"]["next"][/startIndex=(\d+)/, 1] + end + end + + def create_post(options, import_id) + post = super(options, import_id) + if Post === post + add_post(import_id, post) + add_topic(post) + end + post + end + + def process_raw(raw) + doc = Nokogiri::HTML.fragment(raw) + + # convert emoticon + doc.css("span.emoticon-inline").each do |span| + name = span["class"][/emoticon_(\w+)/, 1]&.downcase + name && Emoji.exists?(name) ? span.replace(":#{name}:") : span.remove + end + + # convert mentions + doc.css("a.jive-link-profile-small").each { |a| a.replace("@#{a.content}") } + + # fix links + # doc.css("a[href]").each do |a| + # if a["href"]["#{@base_uri}/docs/DOC-"] + # a["href"] = a["href"][/#{Regexp.escape(@base_uri)}\/docs\/DOC-\d+/] + # elsif a["href"][@base_uri] + # a.replace(a.inner_html) + # end + # end + + html = doc.at(".jive-rendered-content").to_html + + HtmlToMarkdown.new(html).to_markdown + end + + def mark_topics_as_solved + puts "", "Marking topics as solved..." + + PostAction.exec_sql <<-SQL + INSERT INTO topic_custom_fields (name, value, topic_id, created_at, updated_at) + SELECT 'accepted_answer_post_id', pcf.post_id, p.topic_id, p.created_at, p.created_at + FROM post_custom_fields pcf + JOIN posts p ON p.id = pcf.post_id + WHERE pcf.name = 'is_accepted_answer' + SQL + end + + def get(query, authenticated=false) + tries ||= 3 + + command = ["curl", "--silent"] + command << "--user \"#{@username}:#{@password}\"" if authenticated + command << "\"#{@base_uri}/api/core/v3/#{query}\"" + + puts command.join(" ") + + JSON.parse `#{command.join(" ")}` + rescue + retry if (tries -= 1) >= 0 + end + +end + +ImportScripts::JiveApi.new.perform diff --git a/script/import_scripts/phpbb3/importer.rb b/script/import_scripts/phpbb3/importer.rb index b9daaa1568..dfaa21951b 100644 --- a/script/import_scripts/phpbb3/importer.rb +++ b/script/import_scripts/phpbb3/importer.rb @@ -34,9 +34,32 @@ module ImportScripts::PhpBB3 end def change_site_settings + # let's make sure that we import all attachments no matter how big they are + setting_keys = [:max_image_size_kb, :max_attachment_size_kb] + original_validators = disable_setting_validators(setting_keys) + super @importers.permalink_importer.change_site_settings + + enable_setting_validators(original_validators) + end + + def disable_setting_validators(setting_keys) + original_validators = {} + + setting_keys.each do |key| + original_validators[key] = SiteSetting.validators[key] + SiteSetting.validators[key] = nil + end + + original_validators + end + + def enable_setting_validators(original_validators) + original_validators.each do |key, validator| + SiteSetting.validators[key] = validator + end end def get_site_settings_for_import diff --git a/script/profile_db_generator.rb b/script/profile_db_generator.rb index b9de3618ff..79b62064a7 100644 --- a/script/profile_db_generator.rb +++ b/script/profile_db_generator.rb @@ -44,7 +44,7 @@ end def create_admin(seq) User.new.tap { |admin| - admin.email = "admin@localhost#{seq}" + admin.email = "admin@localhost#{seq}.fake" admin.username = "admin#{seq}" admin.password = "password" admin.save diff --git a/spec/components/discourse_plugin_registry_spec.rb b/spec/components/discourse_plugin_registry_spec.rb index 26cb894d46..b280939012 100644 --- a/spec/components/discourse_plugin_registry_spec.rb +++ b/spec/components/discourse_plugin_registry_spec.rb @@ -44,6 +44,15 @@ describe DiscoursePluginRegistry do end end + context '.register_html_builder' do + it "can register and build html" do + DiscoursePluginRegistry.register_html_builder(:my_html) { "my html" } + expect(DiscoursePluginRegistry.build_html(:my_html)).to eq('my html') + DiscoursePluginRegistry.reset! + expect(DiscoursePluginRegistry.build_html(:my_html)).to be_blank + end + end + context '.register_css' do before do registry_instance.register_css('hello.css') diff --git a/spec/components/discourse_sass_compiler_spec.rb b/spec/components/discourse_sass_compiler_spec.rb deleted file mode 100644 index baa998cf3a..0000000000 --- a/spec/components/discourse_sass_compiler_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rails_helper' -require_dependency 'sass/discourse_sass_compiler' - -describe DiscourseSassCompiler do - - let(:test_scss) { "body { p {color: blue;} }\n@import 'common/foundation/variables';\n@import 'plugins';" } - - describe '#compile' do - it "compiles scss" do - DiscoursePluginRegistry.stubs(:stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) - css = described_class.compile(test_scss, "test") - expect(css).to include("color") - expect(css).to include('my-plugin-thing') - end - - it "raises error for invalid scss" do - expect { - described_class.compile("this isn't valid scss", "test") - }.to raise_error(Sass::SyntaxError) - end - - it "doesn't load theme or plugins in safe mode" do - ColorScheme.expects(:enabled).never - DiscoursePluginRegistry.stubs(:stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) - css = described_class.compile(test_scss, "test", safe: true) - expect(css).not_to include('my-plugin-thing') - end - end - -end diff --git a/spec/components/discourse_stylesheets_spec.rb b/spec/components/discourse_stylesheets_spec.rb deleted file mode 100644 index 30562a5844..0000000000 --- a/spec/components/discourse_stylesheets_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'rails_helper' -require_dependency 'sass/discourse_stylesheets' - -describe DiscourseStylesheets do - - describe "compile" do - it "can compile desktop bundle" do - DiscoursePluginRegistry.stubs(:stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) - builder = described_class.new(:desktop) - expect(builder.compile(force: true)).to include('my-plugin-thing') - FileUtils.rm builder.stylesheet_fullpath - end - - it "can compile mobile bundle" do - DiscoursePluginRegistry.stubs(:mobile_stylesheets).returns(["#{Rails.root}/spec/fixtures/scss/my_plugin.scss"]) - builder = described_class.new(:mobile) - expect(builder.compile(force: true)).to include('my-plugin-thing') - FileUtils.rm builder.stylesheet_fullpath - end - - it "can fallback when css is bad" do - DiscoursePluginRegistry.stubs(:stylesheets).returns([ - "#{Rails.root}/spec/fixtures/scss/my_plugin.scss", - "#{Rails.root}/spec/fixtures/scss/broken.scss" - ]) - builder = described_class.new(:desktop) - expect(builder.compile(force: true)).not_to include('my-plugin-thing') - FileUtils.rm builder.stylesheet_fullpath - end - end - - describe "#digest" do - before do - described_class.expects(:max_file_mtime).returns(Time.new(2016, 06, 05, 12, 30, 0, 0)) - end - - it "should return a digest" do - expect(described_class.new.digest).to eq('0e6c2e957cfc92ed60661c90ec3345198ccef887') - end - - it "should include the cdn url when generating the digest" do - GlobalSetting.expects(:cdn_url).returns('https://fastly.maxcdn.org') - expect(described_class.new.digest).to eq('4995163b1232c54c8ed3b44200d803a90bc47613') - end - end -end diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index 3019c5a780..7895e6f4e3 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -157,7 +157,7 @@ describe Email::Receiver do expect(topic.posts.last.cooked).not_to match(/
    HTML reply ;)") + expect(topic.posts.last.raw).to eq("This is a **HTML** reply ;)") expect { process(:hebrew_reply) }.to change { topic.posts.count } expect(topic.posts.last.raw).to eq("שלום! מה שלומך היום?") @@ -177,7 +177,7 @@ describe Email::Receiver do it "prefers html over text when site setting is enabled" do SiteSetting.incoming_email_prefer_html = true expect { process(:text_and_html_reply) }.to change { topic.posts.count } - expect(topic.posts.last.raw).to eq('This is the html part.') + expect(topic.posts.last.raw).to eq('This is the **html** part.') end it "uses text when prefer_html site setting is enabled but no html is available" do @@ -297,6 +297,8 @@ describe Email::Receiver do end it "supports attached images" do + SiteSetting.queue_jobs = true + expect { process(:no_body_with_image) }.to change { topic.posts.count } expect(topic.posts.last.raw).to match(/https://www.eviltrout.com
    ") end diff --git a/spec/components/html_to_markdown_spec.rb b/spec/components/html_to_markdown_spec.rb new file mode 100644 index 0000000000..f71c0e0efc --- /dev/null +++ b/spec/components/html_to_markdown_spec.rb @@ -0,0 +1,169 @@ +require 'rails_helper' +require 'html_to_markdown' + +describe HtmlToMarkdown do + + def html_to_markdown(html) + HtmlToMarkdown.new(html).to_markdown + end + + it "converts " do + expect(html_to_markdown("Strong")).to eq("**Strong**") + expect(html_to_markdown("Str*ng")).to eq("__Str*ng__") + end + + it "converts " do + expect(html_to_markdown("Bold")).to eq("**Bold**") + expect(html_to_markdown("B*ld")).to eq("__B*ld__") + end + + it "converts " do + expect(html_to_markdown("Emphasis")).to eq("*Emphasis*") + expect(html_to_markdown("Emph*sis")).to eq("_Emph*sis_") + end + + it "converts " do + expect(html_to_markdown("Italic")).to eq("*Italic*") + expect(html_to_markdown("It*lic")).to eq("_It*lic_") + end + + it "converts " do + expect(html_to_markdown(%Q{Discourse})).to eq("[Discourse](https://www.discourse.org)") + end + + it "converts " do + expect(html_to_markdown(%Q{Discourse Logo})).to eq("![Discourse Logo](https://www.discourse.org/logo.svg)") + end + + (1..6).each do |n| + it "converts " do + expect(html_to_markdown("Header #{n}")).to eq("#" * n + " Header #{n}") + end + end + + it "converts
    " do + expect(html_to_markdown("Before
    Inside
    After")).to eq("Before\nInside\nAfter") + end + + it "converts
    " do + expect(html_to_markdown("Before
    Inside
    After")).to eq("Before\n\n---\n\nInside\n\n---\n\nAfter") + end + + it "converts " do + expect(html_to_markdown("Teletype")).to eq("`Teletype`") + end + + it "converts " do + expect(html_to_markdown("Code")).to eq("`Code`") + end + + it "supports " do + expect(html_to_markdown("This is an insertion")).to eq("This is an insertion") + end + + it "supports " do + expect(html_to_markdown("This is a deletion")).to eq("This is a deletion") + end + + it "supports " do + expect(html_to_markdown("H2O")).to eq("H2O") + end + + it "supports " do + expect(html_to_markdown("Super Script!")).to eq("Super Script!") + end + + it "supports " do + expect(html_to_markdown("Small")).to eq("Small") + end + + it "supports " do + expect(html_to_markdown("CTRL+C")).to eq("CTRL+C") + end + + it "supports " do + expect(html_to_markdown(%Q{CDCK})).to eq(%Q{CDCK}) + end + + it "supports " do + expect(html_to_markdown("Strike Through")).to eq("Strike Through") + end + + it "supports " do + expect(html_to_markdown("Strike Through")).to eq("Strike Through") + end + + it "supports
    " do + expect(html_to_markdown("
    Quote
    ")).to eq("> Quote") + end + + it "supports
      " do + expect(html_to_markdown("
      • 🍏
      • 🍐
      • 🍌
      ")).to eq("- 🍏\n- 🍐\n- 🍌") + expect(html_to_markdown("
        \n
      • 🍏
      • \n
      • 🍐
      • \n
      • 🍌
      • \n
      ")).to eq("- 🍏\n- 🍐\n- 🍌") + end + + it "supports
        " do + expect(html_to_markdown("
        1. 🍆
        2. 🍅
        3. 🍄
        ")).to eq("1. 🍆\n1. 🍅\n1. 🍄") + end + + it "supports

        inside

      1. " do + expect(html_to_markdown("
        • 🍏

        • 🍐

        • 🍌

        ")).to eq("- 🍏\n\n- 🍐\n\n- 🍌") + end + + it "supports
          inside
            " do + expect(html_to_markdown(<<-HTML +
              +
            • Fruits +
                +
              • 🍏
              • +
              • 🍐
              • +
              • 🍌
              • +
              +
            • +
            • Vegetables +
                +
              • 🍆
              • +
              • 🍅
              • +
              • 🍄
              • +
              +
            • +
            + HTML + )).to eq("- Fruits\n - 🍏\n - 🍐\n - 🍌\n- Vegetables\n - 🍆\n - 🍅\n - 🍄") + end + + it "supports
            " do
            +    expect(html_to_markdown("
            var foo = 'bar';
            ")).to eq("```\nvar foo = 'bar';\n```") + expect(html_to_markdown("
            var foo = 'bar';
            ")).to eq("```\nvar foo = 'bar';\n```") + expect(html_to_markdown(%Q{
            var foo = 'bar';
            })).to eq("```javascript\nvar foo = 'bar';\n```") + end + + it "works" do + expect(html_to_markdown("
            • A list item with a blockquote:

              This is a blockquote
              inside a list item.

            ")).to eq("- A list item with a blockquote:\n\n > This is a **blockquote**\n > inside a list item.") + end + + it "handles

            " do + expect(html_to_markdown("

            1st paragraph

            2nd paragraph

            ")).to eq("1st paragraph\n\n2nd paragraph") + end + + it "handles
            " do + expect(html_to_markdown("
            1st div
            2nd div
            ")).to eq("1st div\n\n2nd div") + end + + it "swallows " do + expect(html_to_markdown("Span")).to eq("Span") + end + + it "swallows " do + expect(html_to_markdown("Underline")).to eq("Underline") + end + + it "removes ")).to eq("") + end + + it "removes ")).to eq("") + end + +end diff --git a/spec/components/onebox/engine/discourse_local_onebox_spec.rb b/spec/components/onebox/engine/discourse_local_onebox_spec.rb index 752eebc6e8..089c07a68b 100644 --- a/spec/components/onebox/engine/discourse_local_onebox_spec.rb +++ b/spec/components/onebox/engine/discourse_local_onebox_spec.rb @@ -71,7 +71,7 @@ describe Onebox::Engine::DiscourseLocalOnebox do it "returns nil if file type is not audio or video" do url = "#{Discourse.base_url}#{path}.pdf" - FakeWeb.register_uri(:get, url, body: "") + stub_request(:get, url).to_return(body: '') expect(Onebox.preview(url).to_s).to eq("") end diff --git a/spec/components/oneboxer_spec.rb b/spec/components/oneboxer_spec.rb index 1df8b30027..a01449f21e 100644 --- a/spec/components/oneboxer_spec.rb +++ b/spec/components/oneboxer_spec.rb @@ -4,9 +4,10 @@ require_dependency 'oneboxer' describe Oneboxer do it "returns blank string for an invalid onebox" do + stub_request(:get, "http://boom.com").to_return(body: "") + expect(Oneboxer.preview("http://boom.com")).to eq("") expect(Oneboxer.onebox("http://boom.com")).to eq("") end end - diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 899ff969ab..7240aeaf80 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -85,6 +85,7 @@ describe PostCreator do DiscourseEvent.expects(:trigger).with(:before_create_topic, anything, anything).once DiscourseEvent.expects(:trigger).with(:after_trigger_post_process, anything).once DiscourseEvent.expects(:trigger).with(:markdown_context, anything).at_least_once + DiscourseEvent.expects(:trigger).with(:topic_notification_level_changed, anything, anything, anything).at_least_once creator.create end @@ -254,6 +255,8 @@ describe PostCreator do it 'creates a post with featured link' do SiteSetting.topic_featured_link_enabled = true SiteSetting.min_first_post_length = 100 + SiteSetting.queue_jobs = true + post = creator_with_featured_link.create expect(post.topic.featured_link).to eq('http://www.discourse.org') expect(post.valid?).to eq(true) @@ -584,7 +587,7 @@ describe PostCreator do it 'acts correctly' do # It's not a warning - expect(post.topic.warning).to be_blank + expect(post.topic.user_warning).to be_blank expect(post.topic.archetype).to eq(Archetype.private_message) expect(post.topic.subtype).to eq(TopicSubtype.user_to_user) @@ -656,11 +659,11 @@ describe PostCreator do topic = post.topic expect(topic).to be_present - expect(topic.warning).to be_present + expect(topic.user_warning).to be_present expect(topic.subtype).to eq(TopicSubtype.moderator_warning) - expect(topic.warning.user).to eq(target_user1) - expect(topic.warning.created_by).to eq(user) - expect(target_user1.warnings.count).to eq(1) + expect(topic.user_warning.user).to eq(target_user1) + expect(topic.user_warning.created_by).to eq(user) + expect(target_user1.user_warnings.count).to eq(1) end end @@ -896,6 +899,23 @@ describe PostCreator do topic_user = TopicUser.find_by(user_id: user.id, topic_id: post.topic_id) expect(topic_user.notification_level).to eq(TopicUser.notification_levels[:tracking]) end + + it "topic notification level is normal based on preference" do + user.user_option.notification_level_when_replying = 1 + + admin = Fabricate(:admin) + topic = PostCreator.create(admin, + title: "this is the title of a topic created by an admin for tracking notification", + raw: "this is the content of a topic created by an admin for keeping a tracking notification state on a topic ;)" + ) + + post = PostCreator.create(user, + topic_id: topic.topic_id, + raw: "this is a reply to set the tracking state to normal ;)" + ) + topic_user = TopicUser.find_by(user_id: user.id, topic_id: post.topic_id) + expect(topic_user.notification_level).to eq(TopicUser.notification_levels[:regular]) + end end describe '#create!' do diff --git a/spec/components/post_destroyer_spec.rb b/spec/components/post_destroyer_spec.rb index 58781bd2f1..37b35ab285 100644 --- a/spec/components/post_destroyer_spec.rb +++ b/spec/components/post_destroyer_spec.rb @@ -467,4 +467,3 @@ describe PostDestroyer do end end - diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 8554b82c33..8b0dfae4e9 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -124,12 +124,28 @@ HTML expect(PrettyText.excerpt("",100)).to eq("[image]") end - it "should keep alt tags" do - expect(PrettyText.excerpt("car",100)).to eq("[car]") + context 'alt tags' do + it "should keep alt tags" do + expect(PrettyText.excerpt("car", 100)).to eq("[car]") + end + + describe 'when alt tag is empty' do + it "should not keep alt tags" do + expect(PrettyText.excerpt("", 100)).to eq("[#{I18n.t('excerpt_image')}]") + end + end end - it "should keep title tags" do - expect(PrettyText.excerpt("",100)).to eq("[car]") + context 'title tags' do + it "should keep title tags" do + expect(PrettyText.excerpt("", 100)).to eq("[car]") + end + + describe 'when title tag is empty' do + it "should not keep title tags" do + expect(PrettyText.excerpt("", 100)).to eq("[#{I18n.t('excerpt_image')}]") + end + end end it "should convert images to markdown if the option is set" do @@ -273,15 +289,23 @@ HTML expect(PrettyText.excerpt(emoji_code, 100)).to eq(":heart:") end - it "should have an option to preserver onebox source" do - onebox = "\n\n\n" - expected = "meta.discourse.org" + context 'option ot preserve onebox source' do + it "should return the right excerpt" do + onebox = "\n\n\n" + expected = "meta.discourse.org" - expect(PrettyText.excerpt(onebox, 100, keep_onebox_source: true)) - .to eq(expected) + expect(PrettyText.excerpt(onebox, 100, keep_onebox_source: true)) + .to eq(expected) - expect(PrettyText.excerpt("#{onebox}\n \n \n \n\n\n #{onebox}", 100, keep_onebox_source: true)) - .to eq("#{expected}\n\n#{expected}") + expect(PrettyText.excerpt("#{onebox}\n \n \n \n\n\n #{onebox}", 100, keep_onebox_source: true)) + .to eq("#{expected}\n\n#{expected}") + end + + it 'should continue to strip quotes' do + expect(PrettyText.excerpt( + "boom", 100, keep_onebox_source: true + )).to eq("boom") + end end end diff --git a/spec/components/site_setting_extension_spec.rb b/spec/components/site_setting_extension_spec.rb index e981f4fbf7..353c04bd4c 100644 --- a/spec/components/site_setting_extension_spec.rb +++ b/spec/components/site_setting_extension_spec.rb @@ -58,7 +58,7 @@ describe SiteSettingExtension do settings.hello = 100 expect(settings.hello).to eq(100) - settings.provider.save(:hello, 99, SiteSetting.types[:fixnum] ) + settings.provider.save(:hello, 99, SiteSetting.types[:integer] ) settings.refresh! expect(settings.hello).to eq(99) @@ -386,16 +386,6 @@ describe SiteSettingExtension do end end - describe "set for an invalid fixnum value" do - it "raises an error" do - settings.setting(:test_setting, 80) - settings.refresh! - expect { - settings.set("test_setting", 9999999999999999999) - }.to raise_error(ArgumentError) - end - end - describe "filter domain name" do before do settings.setting(:white_listed_spam_host_domains, "www.example.com") diff --git a/spec/components/stats_socket_spec.rb b/spec/components/stats_socket_spec.rb new file mode 100644 index 0000000000..cac144439e --- /dev/null +++ b/spec/components/stats_socket_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' +require_dependency 'stats_socket' + +describe StatsSocket do + let :socket_path do + "#{Dir.tmpdir}/#{SecureRandom.hex}" + end + + let :stats_socket do + StatsSocket.new(socket_path) + end + + before do + stats_socket.start + end + + after do + stats_socket.stop + end + + it "can respond to various stats commands" do + line = nil + + # ensure this works more than once :) + 2.times do + socket = UNIXSocket.new(socket_path) + socket.send "gc_stat\n", 0 + line = socket.readline + socket.close + end + + socket = UNIXSocket.new(socket_path) + socket.send "gc_st", 0 + socket.flush + sleep 0.001 + socket.send "at\n", 0 + line = socket.readline + socket.close + + parsed = JSON.parse(line) + + expect(parsed.keys.sort).to eq(GC.stat.keys.map(&:to_s).sort) + end + +end diff --git a/spec/components/step_updater_spec.rb b/spec/components/step_updater_spec.rb index eed8e7f7f5..783d2a1d58 100644 --- a/spec/components/step_updater_spec.rb +++ b/spec/components/step_updater_spec.rb @@ -151,27 +151,31 @@ describe Wizard::StepUpdater do let!(:color_scheme) { Fabricate(:color_scheme, name: 'existing', via_wizard: true) } it "updates the scheme" do - updater = wizard.create_updater('colors', theme_id: 'dark') + updater = wizard.create_updater('colors', base_scheme_id: 'dark') updater.update expect(updater.success?).to eq(true) expect(wizard.completed_steps?('colors')).to eq(true) - color_scheme.reload - expect(color_scheme).to be_enabled + + theme = Theme.find_by(key: SiteSetting.default_theme_key) + expect(theme.color_scheme_id).to eq(color_scheme.id) + end end context "without an existing scheme" do it "creates the scheme" do - updater = wizard.create_updater('colors', theme_id: 'dark') + updater = wizard.create_updater('colors', base_scheme_id: 'dark') updater.update expect(updater.success?).to eq(true) expect(wizard.completed_steps?('colors')).to eq(true) color_scheme = ColorScheme.where(via_wizard: true).first expect(color_scheme).to be_present - expect(color_scheme).to be_enabled expect(color_scheme.colors).to be_present + + theme = Theme.find_by(key: SiteSetting.default_theme_key) + expect(theme.color_scheme_id).to eq(color_scheme.id) end end end diff --git a/spec/components/stylesheet/compiler_spec.rb b/spec/components/stylesheet/compiler_spec.rb new file mode 100644 index 0000000000..5bd19ce32d --- /dev/null +++ b/spec/components/stylesheet/compiler_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' +require 'stylesheet/compiler' + +describe Stylesheet::Compiler do + it "can compile desktop mobile and desktop css" do + css,_map = Stylesheet::Compiler.compile_asset("desktop") + expect(css.length).to be > 1000 + + css,_map = Stylesheet::Compiler.compile_asset("mobile") + expect(css.length).to be > 1000 + end + + it "supports asset-url" do + css,_map = Stylesheet::Compiler.compile(".body{background-image: asset-url('foo.png');}","test.scss") + + expect(css).to include("url('/foo.png')") + expect(css).not_to include('asset-url') + end +end + + diff --git a/spec/components/stylesheet/manager_spec.rb b/spec/components/stylesheet/manager_spec.rb new file mode 100644 index 0000000000..8634216cc3 --- /dev/null +++ b/spec/components/stylesheet/manager_spec.rb @@ -0,0 +1,69 @@ +require 'rails_helper' +require 'stylesheet/compiler' + +describe Stylesheet::Manager do + + it 'does not crash for missing theme' do + link = Stylesheet::Manager.stylesheet_link_tag(:embedded_theme) + expect(link).to eq("") + + theme = Theme.create(name: "embedded", user_id: -1) + SiteSetting.default_theme_key = theme.key + + link = Stylesheet::Manager.stylesheet_link_tag(:embedded_theme) + expect(link).not_to eq("") + end + + it 'can correctly compile theme css' do + theme = Theme.new( + name: 'parent', + user_id: -1 + ) + + theme.set_field(:common, "scss", ".common{.scss{color: red;}}") + theme.set_field(:desktop, "scss", ".desktop{.scss{color: red;}}") + theme.set_field(:mobile, "scss", ".mobile{.scss{color: red;}}") + theme.set_field(:common, "embedded_scss", ".embedded{.scss{color: red;}}") + + theme.save! + + + child_theme = Theme.new( + name: 'parent', + user_id: -1, + ) + + child_theme.set_field(:common, "scss", ".child_common{.scss{color: red;}}") + child_theme.set_field(:desktop, "scss", ".child_desktop{.scss{color: red;}}") + child_theme.set_field(:mobile, "scss", ".child_mobile{.scss{color: red;}}") + child_theme.set_field(:common, "embedded_scss", ".child_embedded{.scss{color: red;}}") + child_theme.save! + + theme.add_child_theme!(child_theme) + + old_link = Stylesheet::Manager.stylesheet_link_tag(:desktop_theme, 'all', theme.key) + + manager = Stylesheet::Manager.new(:desktop_theme, theme.key) + manager.compile(force: true) + + css = File.read(manager.stylesheet_fullpath) + _source_map = File.read(manager.source_map_fullpath) + + expect(css).to match(/child_common/) + expect(css).to match(/child_desktop/) + expect(css).to match(/\.common/) + expect(css).to match(/\.desktop/) + + + child_theme.set_field(:desktop, :scss, ".nothing{color: green;}") + child_theme.save! + + new_link = Stylesheet::Manager.stylesheet_link_tag(:desktop_theme, 'all', theme.key) + + expect(new_link).not_to eq(old_link) + + # our theme better have a name with the theme_id as part of it + expect(new_link).to include("/stylesheets/desktop_theme_#{theme.id}_") + end +end + diff --git a/spec/components/validators/integer_setting_validator_spec.rb b/spec/components/validators/integer_setting_validator_spec.rb index d681f2bc63..db0b1cd281 100644 --- a/spec/components/validators/integer_setting_validator_spec.rb +++ b/spec/components/validators/integer_setting_validator_spec.rb @@ -21,9 +21,21 @@ describe IntegerSettingValidator do it "returns true if value is a valid integer" do expect(validator.valid_value?(1)).to eq(true) - expect(validator.valid_value?(-1)).to eq(true) expect(validator.valid_value?('1')).to eq(true) - expect(validator.valid_value?('-1')).to eq(true) + end + + it "defaults min to 0" do + expect(validator.valid_value?(-1)).to eq(false) + expect(validator.valid_value?('-1')).to eq(false) + expect(validator.valid_value?(0)).to eq(true) + expect(validator.valid_value?('0')).to eq(true) + end + + it "defaults max to 20000" do + expect(validator.valid_value?(20001)).to eq(false) + expect(validator.valid_value?('20001')).to eq(false) + expect(validator.valid_value?(20000)).to eq(true) + expect(validator.valid_value?('20000')).to eq(true) end end @@ -85,5 +97,14 @@ describe IntegerSettingValidator do expect(validator.valid_value?(-2)).to eq(false) end end + + context "when setting is hidden" do + subject(:validator) { described_class.new(hidden: true) } + + it "does not impose default validations" do + expect(validator.valid_value?(-1)).to eq(true) + expect(validator.valid_value?(20001)).to eq(true) + end + end end end diff --git a/spec/components/validators/post_validator_spec.rb b/spec/components/validators/post_validator_spec.rb index e0f984b1e5..25893f34e5 100644 --- a/spec/components/validators/post_validator_spec.rb +++ b/spec/components/validators/post_validator_spec.rb @@ -5,13 +5,41 @@ describe Validators::PostValidator do let(:post) { build(:post) } let(:validator) { Validators::PostValidator.new({}) } - context "when empty raw can bypass post body validation" do - let(:validator) { Validators::PostValidator.new(skip_post_body: true) } - - it "should be allowed for empty raw based on site setting" do + context "#post_body_validator" do + it 'should not allow a post with an empty raw' do post.raw = "" validator.post_body_validator(post) - expect(post.errors).to be_empty + expect(post.errors).to_not be_empty + end + + context "when empty raw can bypass validation" do + let(:validator) { Validators::PostValidator.new(skip_post_body: true) } + + it "should be allowed for empty raw based on site setting" do + post.raw = "" + validator.post_body_validator(post) + expect(post.errors).to be_empty + end + end + + describe "when post's topic is a PM between a human and a non human user" do + let(:robot) { Fabricate(:user, id: -3) } + let(:user) { Fabricate(:user) } + + let(:topic) do + Fabricate(:private_message_topic, topic_allowed_users: [ + Fabricate.build(:topic_allowed_user, user: robot), + Fabricate.build(:topic_allowed_user, user: user) + ]) + end + + it 'should allow a post with an empty raw' do + post = Fabricate.build(:post, topic: topic) + post.raw = "" + validator.post_body_validator(post) + + expect(post.errors).to be_empty + end end end diff --git a/spec/controllers/admin/color_schemes_controller_spec.rb b/spec/controllers/admin/color_schemes_controller_spec.rb index 9059330b44..c1eb2c9abb 100644 --- a/spec/controllers/admin/color_schemes_controller_spec.rb +++ b/spec/controllers/admin/color_schemes_controller_spec.rb @@ -9,7 +9,6 @@ describe Admin::ColorSchemesController do let!(:user) { log_in(:admin) } let(:valid_params) { { color_scheme: { name: 'Such Design', - enabled: true, colors: [ {name: 'primary', hex: 'FFBB00'}, {name: 'secondary', hex: '888888'} diff --git a/spec/controllers/admin/email_controller_spec.rb b/spec/controllers/admin/email_controller_spec.rb index 86d2b42e9a..b14b6ae979 100644 --- a/spec/controllers/admin/email_controller_spec.rb +++ b/spec/controllers/admin/email_controller_spec.rb @@ -71,4 +71,16 @@ describe Admin::EmailController do end end + context '#handle_mail' do + before do + log_in_user(Fabricate(:admin)) + SiteSetting.queue_jobs = true + end + + it 'should enqueue the right job' do + expect { xhr :post, :handle_mail, email: email('cc') } + .to change { Jobs::ProcessEmail.jobs.count }.by(1) + end + end + end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 64fd305e0e..bb7ebbf71a 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -42,7 +42,8 @@ describe Admin::GroupsController do "bio_cooked"=>nil, "public"=>false, "allow_membership_requests"=>false, - "full_name"=>group.full_name + "full_name"=>group.full_name, + "default_notification_level"=>3 }]) end @@ -68,6 +69,11 @@ describe Admin::GroupsController do expect(user2.primary_group).to eq(group) expect(user2.title).to eq("WAT") expect(user2.trust_level).to eq(4) + + # verify JSON response + json = ::JSON.parse(response.body) + expect(json['message']).to eq("2 users have been added to the group.") + expect(json['users_not_added'][0]).to eq("doesnt_exist") end end diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 92da083f32..9d34ebbe1c 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -1,7 +1,6 @@ require 'rails_helper' describe Admin::ReportsController do - it "is a subclass of AdminController" do expect(Admin::ReportsController < Admin::AdminController).to eq(true) end @@ -58,6 +57,46 @@ describe Admin::ReportsController do end + describe 'when report is scoped to a category' do + let(:category) { Fabricate(:category) } + let(:topic) { Fabricate(:topic, category: category) } + let(:other_topic) { Fabricate(:topic) } + + it 'should render the report as JSON' do + topic + other_topic + + xhr :get, :show, type: 'topics', category_id: category.id + + expect(response).to be_success + + report = JSON.parse(response.body)["report"] + + expect(report["type"]).to eq('topics') + expect(report["data"].count).to eq(1) + end + end + + describe 'when report is scoped to a group' do + let(:user) { Fabricate(:user) } + let(:other_user) { Fabricate(:user) } + let(:group) { Fabricate(:group) } + + it 'should render the report as JSON' do + other_user + group.add(user) + + xhr :get, :show, type: 'signups', group_id: group.id + + expect(response).to be_success + + report = JSON.parse(response.body)["report"] + + expect(report["type"]).to eq('signups') + expect(report["data"].count).to eq(1) + end + end + end end diff --git a/spec/controllers/admin/site_customizations_controller_spec.rb b/spec/controllers/admin/site_customizations_controller_spec.rb deleted file mode 100644 index 2695f17c7e..0000000000 --- a/spec/controllers/admin/site_customizations_controller_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'rails_helper' - -describe Admin::SiteCustomizationsController do - - it "is a subclass of AdminController" do - expect(Admin::UsersController < Admin::AdminController).to eq(true) - end - - context 'while logged in as an admin' do - before do - @user = log_in(:admin) - end - - context ' .index' do - it 'returns success' do - SiteCustomization.create!(name: 'my name', user_id: Fabricate(:user).id, header: "my awesome header", stylesheet: "my awesome css") - xhr :get, :index - expect(response).to be_success - end - - it 'returns JSON' do - xhr :get, :index - expect(::JSON.parse(response.body)).to be_present - end - end - - context ' .create' do - it 'returns success' do - xhr :post, :create, site_customization: {name: 'my test name'} - expect(response).to be_success - end - - it 'returns json' do - xhr :post, :create, site_customization: {name: 'my test name'} - expect(::JSON.parse(response.body)).to be_present - end - - it 'logs the change' do - StaffActionLogger.any_instance.expects(:log_site_customization_change).once - xhr :post, :create, site_customization: {name: 'my test name'} - end - end - - end - - - -end diff --git a/spec/controllers/admin/staff_action_logs_controller_spec.rb b/spec/controllers/admin/staff_action_logs_controller_spec.rb index a566571708..6f8b64feca 100644 --- a/spec/controllers/admin/staff_action_logs_controller_spec.rb +++ b/spec/controllers/admin/staff_action_logs_controller_spec.rb @@ -8,15 +8,35 @@ describe Admin::StaffActionLogsController do let!(:user) { log_in(:admin) } context '.index' do - before do + + it 'works' do xhr :get, :index + expect(response).to be_success + expect(::JSON.parse(response.body)).to be_a(Array) end + end - subject { response } - it { is_expected.to be_success } + context '.diff' do + it 'can generate diffs for theme changes' do + theme = Theme.new(user_id: -1, name: 'bob') + theme.set_field(:mobile, :scss, 'body {.up}') + theme.set_field(:common, :scss, 'omit-dupe') - it 'returns JSON' do - expect(::JSON.parse(subject.body)).to be_a(Array) + original_json = ThemeSerializer.new(theme, root: false).to_json + + theme.set_field(:mobile, :scss, 'body {.down}') + + record = StaffActionLogger.new(Discourse.system_user) + .log_theme_change(original_json, theme) + + xhr :get, :diff, id: record.id + expect(response).to be_success + + parsed = JSON.parse(response.body) + expect(parsed["side_by_side"]).to include("up") + expect(parsed["side_by_side"]).to include("down") + + expect(parsed["side_by_side"]).not_to include("omit-dupe") end end end diff --git a/spec/controllers/admin/themes_controller_spec.rb b/spec/controllers/admin/themes_controller_spec.rb new file mode 100644 index 0000000000..4280c4d5ca --- /dev/null +++ b/spec/controllers/admin/themes_controller_spec.rb @@ -0,0 +1,104 @@ +require 'rails_helper' + +describe Admin::ThemesController do + + it "is a subclass of AdminController" do + expect(Admin::UsersController < Admin::AdminController).to eq(true) + end + + context 'while logged in as an admin' do + before do + @user = log_in(:admin) + end + + context ' .index' do + it 'returns success' do + theme = Theme.new(name: 'my name', user_id: -1) + theme.set_field(:common, :scss, '.body{color: black;}') + theme.set_field(:desktop, :after_header, 'test') + + theme.remote_theme = RemoteTheme.new( + remote_url: 'awesome.git', + remote_version: '7', + local_version: '8', + remote_updated_at: Time.zone.now + ) + + theme.save! + + # this will get serialized as well + ColorScheme.create_from_base(name: "test", colors: []) + + xhr :get, :index + + expect(response).to be_success + + json = ::JSON.parse(response.body) + + expect(json["extras"]["color_schemes"].length).to eq(2) + theme_json = json["themes"].find{|t| t["id"] == theme.id} + expect(theme_json["theme_fields"].length).to eq(2) + expect(theme_json["remote_theme"]["remote_version"]).to eq("7") + end + end + + context ' .create' do + it 'creates a theme' do + xhr :post, :create, theme: {name: 'my test name', theme_fields: [name: 'scss', target: 'common', value: 'body{color: red;}']} + expect(response).to be_success + + json = ::JSON.parse(response.body) + + expect(json["theme"]["theme_fields"].length).to eq(1) + expect(UserHistory.where(action: UserHistory.actions[:change_theme]).count).to eq(1) + end + end + + context ' .update' do + it 'can change default theme' do + theme = Theme.create(name: 'my name', user_id: -1) + xhr :put, :update, id: theme.id, theme: { default: true } + expect(SiteSetting.default_theme_key).to eq(theme.key) + end + + it 'can unset default theme' do + theme = Theme.create(name: 'my name', user_id: -1) + SiteSetting.default_theme_key = theme.key + xhr :put, :update, id: theme.id, theme: { default: false} + expect(SiteSetting.default_theme_key).to be_blank + end + + it 'updates a theme' do + #focus + theme = Theme.new(name: 'my name', user_id: -1) + theme.set_field(:common, :scss, '.body{color: black;}') + theme.save + + child_theme = Theme.create(name: 'my name', user_id: -1) + + xhr :put, :update, id: theme.id, + theme: { + child_theme_ids: [child_theme.id], + name: 'my test name', + theme_fields: [ + { name: 'scss', target: 'common', value: '' }, + { name: 'scss', target: 'desktop', value: 'body{color: blue;}' } + ] + } + expect(response).to be_success + + json = ::JSON.parse(response.body) + + fields = json["theme"]["theme_fields"] + + expect(fields.first["value"]).to eq('body{color: blue;}') + expect(fields.length).to eq(1) + + expect(json["theme"]["child_themes"].length).to eq(1) + + expect(UserHistory.where(action: UserHistory.actions[:change_theme]).count).to eq(1) + end + end + end + +end diff --git a/spec/controllers/email_controller_spec.rb b/spec/controllers/email_controller_spec.rb index 63fd6865ad..996d88cf3e 100644 --- a/spec/controllers/email_controller_spec.rb +++ b/spec/controllers/email_controller_spec.rb @@ -232,6 +232,4 @@ describe EmailController do end end - - end diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 146a007753..9079592775 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -223,20 +223,42 @@ describe InvitesController do end end - context 'welcome message' do + context '.post_process_invite' do before do Invite.any_instance.stubs(:redeem).returns(user) Jobs.expects(:enqueue).with(:invite_email, has_key(:invite_id)) + user.password_hash = nil end it 'sends a welcome message if set' do user.send_welcome_message = true user.expects(:enqueue_welcome_message).with('welcome_invite') + Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_entries(username: user.username)) xhr :put, :perform_accept_invitation, id: invite.invite_key, format: :json end - it "doesn't send a welcome message if not set" do + it "sends password reset email if password is not set" do user.expects(:enqueue_welcome_message).with('welcome_invite').never + Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_entries(username: user.username)) + xhr :put, :perform_accept_invitation, id: invite.invite_key, format: :json + end + + it "does not send password reset email if sso is enabled" do + SiteSetting.enable_sso = true + Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_key(:username)).never + xhr :put, :perform_accept_invitation, id: invite.invite_key, format: :json + end + + it "does not send password reset email if local login is disabled" do + SiteSetting.enable_local_logins = false + Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_key(:username)).never + xhr :put, :perform_accept_invitation, id: invite.invite_key, format: :json + end + + it 'sends an activation email if password is set' do + user.password_hash = 'qaw3ni3h2wyr63lakw7pea1nrtr44pls' + Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_key(:username)).never + Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup, user_id: user.id)) xhr :put, :perform_accept_invitation, id: invite.invite_key, format: :json end end diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb index 9995437519..f521898bf0 100644 --- a/spec/controllers/notifications_controller_spec.rb +++ b/spec/controllers/notifications_controller_spec.rb @@ -36,8 +36,10 @@ describe NotificationsController do context 'when username params is not valid' do it 'should raise the right error' do - expect { xhr :get, :index, username: 'somedude' } - .to raise_error(Discourse::InvalidParameters) + xhr :get, :index, username: 'somedude' + + expect(response).to_not be_success + expect(response.status).to eq(404) end end end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 98b198550a..ccee8bf1e2 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -448,6 +448,12 @@ describe PostsController do let(:post_action) { PostAction.act(user, post, PostActionType.types[:bookmark]) } let(:admin) { Fabricate(:admin) } + it "returns the right response when post is not bookmarked" do + xhr :put, :bookmark, post_id: Fabricate(:post, user: user).id + + expect(response.status).to eq(404) + end + it 'should be able to remove a bookmark' do post_action xhr :put, :bookmark, post_id: post.id diff --git a/spec/controllers/site_customizations_controller_spec.rb b/spec/controllers/site_customizations_controller_spec.rb deleted file mode 100644 index d3a0f17451..0000000000 --- a/spec/controllers/site_customizations_controller_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'rails_helper' - -describe SiteCustomizationsController do - - before do - SiteCustomization.clear_cache! - end - - it 'can deliver enabled css' do - SiteCustomization.create!(name: '1', - user_id: -1, - enabled: true, - mobile_stylesheet: '.a1{margin: 1px;}', - stylesheet: '.b1{margin: 1px;}' - ) - - SiteCustomization.create!(name: '2', - user_id: -1, - enabled: true, - mobile_stylesheet: '.a2{margin: 1px;}', - stylesheet: '.b2{margin: 1px;}' - ) - - get :show, key: SiteCustomization::ENABLED_KEY, format: :css, target: 'mobile' - expect(response.body).to match(/\.a1.*\.a2/m) - - get :show, key: SiteCustomization::ENABLED_KEY, format: :css - expect(response.body).to match(/\.b1.*\.b2/m) - end - - it 'can deliver specific css' do - c = SiteCustomization.create!(name: '1', - user_id: -1, - enabled: true, - mobile_stylesheet: '.a1{margin: 1px;}', - stylesheet: '.b1{margin: 1px;}' - ) - - get :show, key: c.key, format: :css, target: 'mobile' - expect(response.body).to match(/\.a1/) - - get :show, key: c.key, format: :css - expect(response.body).to match(/\.b1/) - end -end diff --git a/spec/controllers/stylesheets_controller_spec.rb b/spec/controllers/stylesheets_controller_spec.rb index 5a5f9cf05f..8f18d2b2d5 100644 --- a/spec/controllers/stylesheets_controller_spec.rb +++ b/spec/controllers/stylesheets_controller_spec.rb @@ -5,19 +5,12 @@ describe StylesheetsController do it 'can survive cache miss' do StylesheetCache.destroy_all - builder = DiscourseStylesheets.new('desktop_rtl') + builder = Stylesheet::Manager.new('desktop_rtl', nil) builder.compile - builder.ensure_digestless_file digest = StylesheetCache.first.digest StylesheetCache.destroy_all - # digestless - get :show, name: 'desktop_rtl' - expect(response).to be_success - - StylesheetCache.destroy_all - get :show, name: "desktop_rtl_#{digest}" expect(response).to be_success @@ -26,10 +19,7 @@ describe StylesheetsController do expect(cached.digest).to eq digest # tmp folder destruction and cached - `rm #{DiscourseStylesheets.cache_fullpath}/*` - - get :show, name: 'desktop_rtl' - expect(response).to be_success + `rm #{Stylesheet::Manager.cache_fullpath}/*` get :show, name: "desktop_rtl_#{digest}" expect(response).to be_success @@ -38,4 +28,31 @@ describe StylesheetsController do end + it 'can lookup theme specific css' do + scheme = ColorScheme.create_from_base({name: "testing", colors: []}) + theme = Theme.create!(name: "test", color_scheme_id: scheme.id, user_id: -1) + + builder = Stylesheet::Manager.new(:desktop, theme.key) + builder.compile + + `rm #{Stylesheet::Manager.cache_fullpath}/*` + + get :show, name: builder.stylesheet_filename.sub(".css", "") + expect(response).to be_success + + get :show, name: builder.stylesheet_filename_no_digest.sub(".css", "") + expect(response).to be_success + + builder = Stylesheet::Manager.new(:desktop_theme, theme.key) + builder.compile + + `rm #{Stylesheet::Manager.cache_fullpath}/*` + + get :show, name: builder.stylesheet_filename.sub(".css", "") + expect(response).to be_success + + get :show, name: builder.stylesheet_filename_no_digest.sub(".css", "") + expect(response).to be_success + end + end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 3dd88514a0..dc1b11c603 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -75,12 +75,12 @@ describe UploadsController do end it 'is successful with synchronous api' do - SiteSetting.stubs(:authorized_extensions).returns("*") + SiteSetting.authorized_extensions = "*" controller.stubs(:is_api?).returns(true) Jobs.expects(:enqueue).with(:create_thumbnails, anything) - FakeWeb.register_uri(:get, "http://example.com/image.png", :body => File.read('spec/fixtures/images/logo.png')) + stub_request(:get, "http://example.com/image.png").to_return(body: File.read('spec/fixtures/images/logo.png')) xhr :post, :create, url: 'http://example.com/image.png', type: "avatar", synchronous: true diff --git a/spec/controllers/user_avatars_controller_spec.rb b/spec/controllers/user_avatars_controller_spec.rb index d09bc36568..603a60a441 100644 --- a/spec/controllers/user_avatars_controller_spec.rb +++ b/spec/controllers/user_avatars_controller_spec.rb @@ -24,7 +24,7 @@ describe UserAvatarsController do SiteSetting.s3_upload_bucket = "test" SiteSetting.s3_cdn_url = "http://cdn.com" - FakeWeb.register_uri(:get, "http://cdn.com/something/else", :body => 'image') + stub_request(:get, "http://cdn.com/something/else").to_return(body: 'image') GlobalSetting.expects(:cdn_url).returns("http://awesome.com/boom") diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 3b2f5f0d5a..b44d22d627 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -13,10 +13,10 @@ describe UsersController do expect(response).to be_success end - it "raises an error for anon when profiles are hidden" do + it "should redirect to login page for anonymous user when profiles are hidden" do SiteSetting.hide_user_profiles_from_public = true xhr :get, :show, username: user.username, format: :json - expect(response).not_to be_success + expect(response).to redirect_to '/login' end end diff --git a/spec/fabricators/color_scheme_fabricator.rb b/spec/fabricators/color_scheme_fabricator.rb index 09bde58ef2..111964527a 100644 --- a/spec/fabricators/color_scheme_fabricator.rb +++ b/spec/fabricators/color_scheme_fabricator.rb @@ -1,5 +1,4 @@ Fabricator(:color_scheme) do name { sequence(:name) {|i| "Palette #{i}" } } - enabled false color_scheme_colors(count: 2) { |attrs, i| Fabricate.build(:color_scheme_color, color_scheme: nil) } end diff --git a/spec/fabricators/post_fabricator.rb b/spec/fabricators/post_fabricator.rb index e462230efc..4a28dac1c0 100644 --- a/spec/fabricators/post_fabricator.rb +++ b/spec/fabricators/post_fabricator.rb @@ -126,8 +126,8 @@ Fabricator(:private_message_post, from: :post) do created_at: attrs[:created_at], subtype: TopicSubtype.user_to_user, topic_allowed_users: [ - Fabricate.build(:topic_allowed_user, user_id: attrs[:user].id), - Fabricate.build(:topic_allowed_user, user_id: Fabricate(:user).id) + Fabricate.build(:topic_allowed_user, user: attrs[:user]), + Fabricate.build(:topic_allowed_user, user: Fabricate(:user)) ] ) end diff --git a/spec/fabricators/topic_allowed_group_fabricator.rb b/spec/fabricators/topic_allowed_group_fabricator.rb new file mode 100644 index 0000000000..e864faf6f9 --- /dev/null +++ b/spec/fabricators/topic_allowed_group_fabricator.rb @@ -0,0 +1,4 @@ +Fabricator(:topic_allowed_group) do + topic + group +end diff --git a/spec/fabricators/topic_allowed_user_fabricator.rb b/spec/fabricators/topic_allowed_user_fabricator.rb new file mode 100644 index 0000000000..27c08d78b3 --- /dev/null +++ b/spec/fabricators/topic_allowed_user_fabricator.rb @@ -0,0 +1,3 @@ +Fabricator(:topic_allowed_user) do + user +end diff --git a/spec/fabricators/topic_fabricator.rb b/spec/fabricators/topic_fabricator.rb index bd6eff9413..e603a1244f 100644 --- a/spec/fabricators/topic_fabricator.rb +++ b/spec/fabricators/topic_fabricator.rb @@ -12,9 +12,6 @@ Fabricator(:closed_topic, from: :topic) do closed true end -Fabricator(:topic_allowed_user) do -end - Fabricator(:banner_topic, from: :topic) do archetype Archetype.banner end @@ -25,7 +22,7 @@ Fabricator(:private_message_topic, from: :topic) do title { sequence(:title) { |i| "This is a private message #{i}" } } archetype "private_message" topic_allowed_users{|t| [ - Fabricate.build(:topic_allowed_user, user_id: t[:user].id), - Fabricate.build(:topic_allowed_user, user_id: Fabricate(:coding_horror).id) + Fabricate.build(:topic_allowed_user, user: t[:user]), + Fabricate.build(:topic_allowed_user, user: Fabricate(:coding_horror)) ]} end diff --git a/spec/integration/admin/emojis_spec.rb b/spec/integration/admin/emojis_spec.rb index 5ea92ea6f3..c0d72fcdfb 100644 --- a/spec/integration/admin/emojis_spec.rb +++ b/spec/integration/admin/emojis_spec.rb @@ -16,7 +16,7 @@ RSpec.describe "Managing custom emojis" do name: 'test', file: fixture_file_upload("#{Rails.root}/spec/fixtures/images/fake.jpg") }) - end.first + end.find{|m| m.channel == "/uploads/emoji"} expect(message.channel).to eq("/uploads/emoji") expect(message.data["errors"]).to eq([I18n.t('upload.images.size_not_found')]) @@ -32,7 +32,7 @@ RSpec.describe "Managing custom emojis" do name: 'test', file: fixture_file_upload("#{Rails.root}/spec/fixtures/images/logo.png") }) - end.first + end.find{|m| m.channel == "/uploads/emoji"} expect(message.channel).to eq("/uploads/emoji") @@ -50,7 +50,7 @@ RSpec.describe "Managing custom emojis" do name: 'test', file: fixture_file_upload("#{Rails.root}/spec/fixtures/images/logo.png") }) - end.first + end.find{|m| m.channel == "/uploads/emoji"} custom_emoji = CustomEmoji.last upload = custom_emoji.upload diff --git a/spec/integration/managing_topic_status_spec.rb b/spec/integration/managing_topic_status_spec.rb index c283ed60c1..69f55d7b79 100644 --- a/spec/integration/managing_topic_status_spec.rb +++ b/spec/integration/managing_topic_status_spec.rb @@ -78,6 +78,8 @@ RSpec.describe "Managing a topic's status update", type: :request do describe 'publishing topic to category in the future' do it 'should be able to create the topic status update' do + SiteSetting.queue_jobs = true + post "/t/#{topic.id}/status_update.json", time: 24, status_type: TopicStatusUpdate.types[3], diff --git a/spec/jobs/clean_up_unused_staged_users.rb b/spec/jobs/clean_up_unused_staged_users.rb new file mode 100644 index 0000000000..2fd222c0e8 --- /dev/null +++ b/spec/jobs/clean_up_unused_staged_users.rb @@ -0,0 +1,37 @@ +require 'rails_helper' + +RSpec.describe Jobs::CleanUpUnusedStagedUsers do + let(:user) { Fabricate(:user) } + let(:staged_user) { Fabricate(:user, staged: true) } + + context 'when staged user is unused' do + it 'should clean up the staged user' do + user + staged_user.update!(created_at: 2.years.ago) + + expect { described_class.new.execute({}) }.to change { User.count }.by(-1) + expect(User.find_by(id: staged_user.id)).to eq(nil) + end + + describe 'when staged user is not old enough' do + it 'should not clean up the staged user' do + user + staged_user.update!(created_at: 5.months.ago) + + expect { described_class.new.execute({}) }.to_not change { User.count } + expect(User.find_by(id: staged_user.id)).to eq(staged_user) + end + end + end + + context 'when staged user is not unused' do + it 'should not clean up the staged user' do + user + Fabricate(:post, user: staged_user) + user.update!(created_at: 2.years.ago) + + expect { described_class.new.execute({}) }.to_not change { User.count } + expect(User.find_by(id: staged_user.id)).to eq(staged_user) + end + end +end diff --git a/spec/jobs/enqueue_digest_emails_spec.rb b/spec/jobs/enqueue_digest_emails_spec.rb index 9d00e3dd4c..acb3f16403 100644 --- a/spec/jobs/enqueue_digest_emails_spec.rb +++ b/spec/jobs/enqueue_digest_emails_spec.rb @@ -6,7 +6,7 @@ describe Jobs::EnqueueDigestEmails do describe '#target_users' do context 'disabled digests' do - before { SiteSetting.stubs(:default_email_digest_frequency).returns(0) } + before { SiteSetting.default_email_digest_frequency = 0 } let!(:user_no_digests) { Fabricate(:active_user, last_emailed_at: 8.days.ago, last_seen_at: 10.days.ago) } it "doesn't return users with email disabled" do @@ -129,13 +129,24 @@ describe Jobs::EnqueueDigestEmails do end end + context "private email" do + before do + Jobs::EnqueueDigestEmails.any_instance.expects(:target_user_ids).never + SiteSetting.private_email = true + Jobs.expects(:enqueue).with(:user_email, type: :digest, user_id: user.id).never + end + it "doesn't return users with email disabled" do + Jobs::EnqueueDigestEmails.new.execute({}) + end + end + context "digest emails are disabled" do before do Jobs::EnqueueDigestEmails.any_instance.expects(:target_user_ids).never + SiteSetting.disable_digest_emails = true end it "does not enqueue the digest email job" do - SiteSetting.stubs(:disable_digest_emails?).returns(true) Jobs.expects(:enqueue).with(:user_email, type: :digest, user_id: user.id).never Jobs::EnqueueDigestEmails.new.execute({}) end diff --git a/spec/jobs/pending_flags_reminder_spec.rb b/spec/jobs/pending_flags_reminder_spec.rb index e73038c0fb..25f6eb673b 100644 --- a/spec/jobs/pending_flags_reminder_spec.rb +++ b/spec/jobs/pending_flags_reminder_spec.rb @@ -4,15 +4,22 @@ describe Jobs::PendingFlagsReminder do context "notify_about_flags_after is 0" do before { SiteSetting.stubs(:notify_about_flags_after).returns(0) } - it "never emails" do + it "never notifies" do PostAction.stubs(:flagged_posts_count).returns(1) - Email::Sender.any_instance.expects(:send).never + PostCreator.expects(:create).never described_class.new.execute({}) end end context "notify_about_flags_after is 48" do - before { SiteSetting.stubs(:notify_about_flags_after).returns(48) } + before do + SiteSetting.notify_about_flags_after = 48 + $redis.del described_class.last_notified_key + end + + after do + $redis.del described_class.last_notified_key + end it "doesn't send message when flags are less than 48 hours old" do Fabricate(:flag, created_at: 47.hours.ago) @@ -27,5 +34,16 @@ describe Jobs::PendingFlagsReminder do PostCreator.expects(:create).once.returns(true) described_class.new.execute({}) end + + it "doesn't send a message if there are no new flags older than 48 hours old" do + old_flag = Fabricate(:flag, created_at: 50.hours.ago) + new_flag = Fabricate(:flag, created_at: 47.hours.ago) + PostAction.stubs(:flagged_posts_count).returns(2) + job = described_class.new + job.last_notified_id = old_flag.id + PostCreator.expects(:create).never + job.execute({}) + expect(job.last_notified_id).to eq(old_flag.id) + end end end diff --git a/spec/jobs/pending_queued_posts_reminder_spec.rb b/spec/jobs/pending_queued_posts_reminder_spec.rb index dd182686da..fe5a1c7956 100644 --- a/spec/jobs/pending_queued_posts_reminder_spec.rb +++ b/spec/jobs/pending_queued_posts_reminder_spec.rb @@ -12,7 +12,9 @@ describe Jobs::PendingQueuedPostReminder do end context "notify_about_queued_posts_after is 24" do - before { SiteSetting.stubs(:notify_about_queued_posts_after).returns(24) } + before do + SiteSetting.notify_about_queued_posts_after = 24 + end it "doesn't email if there are no queued posts" do described_class.any_instance.stubs(:should_notify_ids).returns([]) diff --git a/spec/jobs/publish_topic_to_category_spec.rb b/spec/jobs/publish_topic_to_category_spec.rb index e628d71998..ab4470c560 100644 --- a/spec/jobs/publish_topic_to_category_spec.rb +++ b/spec/jobs/publish_topic_to_category_spec.rb @@ -37,7 +37,7 @@ RSpec.describe Jobs::PublishTopicToCategory do end end - it 'should publish the topic to the new category correctly' do + it 'should publish the topic to the new category' do Timecop.travel(1.hour.ago) { topic.update!(visible: false) } message = MessageBus.track_publish do @@ -47,6 +47,7 @@ RSpec.describe Jobs::PublishTopicToCategory do topic.reload expect(topic.category).to eq(another_category) expect(topic.visible).to eq(true) + expect(TopicStatusUpdate.find_by(id: topic.topic_status_update.id)).to eq(nil) %w{created_at bumped_at updated_at last_posted_at}.each do |attribute| expect(topic.public_send(attribute)).to be_within(1.second).of(Time.zone.now) @@ -55,4 +56,32 @@ RSpec.describe Jobs::PublishTopicToCategory do expect(message.data[:reload_topic]).to be_present expect(message.data[:refresh_stream]).to be_present end + + describe 'when topic is a private message' do + before do + Timecop.travel(1.hour.ago) do + expect { topic.convert_to_private_message(Discourse.system_user) } + .to change { topic.private_message? }.to(true) + end + end + + + it 'should publish the topic to the new category' do + message = MessageBus.track_publish do + described_class.new.execute(topic_status_update_id: topic.topic_status_update.id) + end.last + + topic.reload + expect(topic.category).to eq(another_category) + expect(topic.visible).to eq(true) + expect(topic.private_message?).to eq(false) + + %w{created_at bumped_at updated_at last_posted_at}.each do |attribute| + expect(topic.public_send(attribute)).to be_within(1.second).of(Time.zone.now) + end + + expect(message.data[:reload_topic]).to be_present + expect(message.data[:refresh_stream]).to be_present + end + end end diff --git a/spec/jobs/pull_hotlinked_images_spec.rb b/spec/jobs/pull_hotlinked_images_spec.rb index aa9473a478..bdae4fd3d4 100644 --- a/spec/jobs/pull_hotlinked_images_spec.rb +++ b/spec/jobs/pull_hotlinked_images_spec.rb @@ -5,8 +5,9 @@ describe Jobs::PullHotlinkedImages do before do png = Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") - FakeWeb.register_uri(:get, "http://wiki.mozilla.org/images/2/2e/Longcat1.png", body: png) + stub_request(:get, "http://wiki.mozilla.org/images/2/2e/Longcat1.png").to_return(body: png) SiteSetting.download_remote_images_to_local = true + FastImage.expects(:size).returns([100, 100]).at_least_once end it 'replaces image src' do diff --git a/spec/jobs/update_gravatar_spec.rb b/spec/jobs/update_gravatar_spec.rb index 0c80a3afcf..bba53c724a 100644 --- a/spec/jobs/update_gravatar_spec.rb +++ b/spec/jobs/update_gravatar_spec.rb @@ -8,7 +8,7 @@ describe Jobs::UpdateGravatar do expect(user.user_avatar.gravatar_upload_id).to eq(nil) png = Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") - FakeWeb.register_uri(:get, "http://www.gravatar.com/avatar/d10ca8d11301c2f4993ac2279ce4b930.png?s=500&d=404", body: png) + stub_request(:get, "http://www.gravatar.com/avatar/d10ca8d11301c2f4993ac2279ce4b930.png?s=360&d=404").to_return(body: png) SiteSetting.automatically_download_gravatars = true diff --git a/spec/mailers/invite_mailer_spec.rb b/spec/mailers/invite_mailer_spec.rb index 3dc8a007f1..68ce2b4a4a 100644 --- a/spec/mailers/invite_mailer_spec.rb +++ b/spec/mailers/invite_mailer_spec.rb @@ -67,6 +67,7 @@ describe InviteMailer do it 'renders invite link' do expect(custom_invite_mail.body.encoded).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") end + end end end @@ -111,6 +112,14 @@ describe InviteMailer do it 'renders topic title' do expect(invite_mail.body.encoded).to match(topic.title) end + + it "respects the private_email setting" do + SiteSetting.private_email = true + + message = invite_mail + expect(message.body.to_s).not_to include(topic.title) + expect(message.body.to_s).not_to include(topic.slug) + end end context "custom invite message" do diff --git a/spec/mailers/user_notifications_spec.rb b/spec/mailers/user_notifications_spec.rb index 3cd4ca74b4..f37d78d78d 100644 --- a/spec/mailers/user_notifications_spec.rb +++ b/spec/mailers/user_notifications_spec.rb @@ -47,7 +47,10 @@ describe UserNotifications do user.user_option.update_columns(email_previous_replies: UserOption.previous_replies_type[:always]) expect(UserNotifications.get_context_posts(post3, topic_user, user).count).to eq(2) + SiteSetting.private_email = true + expect(UserNotifications.get_context_posts(post3, topic_user, user).count).to eq(0) end + end describe ".signup" do @@ -152,6 +155,15 @@ describe UserNotifications do expect(subject.html_part.body.to_s).to include old_topic.title expect(subject.html_part.body.to_s).to include whisper.cooked end + + it "hides details for private email" do + SiteSetting.private_email = true + + expect(subject.html_part.body.to_s).not_to include(topic.title) + expect(subject.html_part.body.to_s).not_to include(topic.slug) + expect(subject.text_part.body.to_s).not_to include(topic.title) + expect(subject.text_part.body.to_s).not_to include(topic.slug) + end end end @@ -291,8 +303,6 @@ describe UserNotifications do expect(mail.html_part.to_s.scan(/In Reply To/).count).to eq(0) - - SiteSetting.enable_names = true SiteSetting.display_name_on_posts = true SiteSetting.prioritize_username_in_ux = false @@ -324,6 +334,21 @@ describe UserNotifications do expect(mail_html.scan(/>Bob Marley/).count).to eq(0) expect(mail_html.scan(/>bobmarley/).count).to eq(1) end + + it "doesn't include details when private_email is enabled" do + SiteSetting.private_email = true + mail = UserNotifications.user_replied( + response.user, + post: response, + notification_type: notification.notification_type, + notification_data_hash: notification.data_hash + ) + + expect(mail.html_part.to_s).to_not include(response.raw) + expect(mail.html_part.to_s).to_not include(topic.url) + expect(mail.text_part.to_s).to_not include(response.raw) + expect(mail.text_part.to_s).to_not include(topic.url) + end end describe '.user_posted' do @@ -360,6 +385,19 @@ describe UserNotifications do tu = TopicUser.get(post.topic_id, response.user) expect(tu.last_emailed_post_number).to eq(response.post_number) end + + it "doesn't include details when private_email is enabled" do + SiteSetting.private_email = true + mail = UserNotifications.user_posted( + response.user, + post: response, + notification_type: notification.notification_type, + notification_data_hash: notification.data_hash + ) + + expect(mail.html_part.to_s).to_not include(response.raw) + expect(mail.text_part.to_s).to_not include(response.raw) + end end describe '.user_private_message' do @@ -397,6 +435,21 @@ describe UserNotifications do tu = TopicUser.get(topic.id, response.user) expect(tu.last_emailed_post_number).to eq(response.post_number) end + + it "doesn't include details when private_email is enabled" do + SiteSetting.private_email = true + mail = UserNotifications.user_private_message( + response.user, + post: response, + notification_type: notification.notification_type, + notification_data_hash: notification.data_hash + ) + + expect(mail.html_part.to_s).to_not include(response.raw) + expect(mail.html_part.to_s).to_not include(topic.url) + expect(mail.text_part.to_s).to_not include(response.raw) + expect(mail.text_part.to_s).to_not include(topic.url) + end end @@ -449,6 +502,28 @@ describe UserNotifications do end end + shared_examples "respect for private_email" do + context "private_email" do + it "doesn't support reply by email" do + SiteSetting.private_email = true + mailer = UserNotifications.send( + mail_type, + user, + notification_type: Notification.types[notification.notification_type], + notification_data_hash: notification.data_hash, + post: notification.post + ) + message = mailer.message + + topic = notification.post.topic + expect(message.html_part.body.to_s).not_to include(topic.title) + expect(message.html_part.body.to_s).not_to include(topic.slug) + expect(message.text_part.body.to_s).not_to include(topic.title) + expect(message.text_part.body.to_s).not_to include(topic.slug) + end + end + end + # The parts of emails that are derived from templates are translated shared_examples "sets user locale" do context "set locale for translating templates" do @@ -546,6 +621,7 @@ describe UserNotifications do describe "user mentioned email" do include_examples "notification email building" do let(:notification_type) { :mentioned } + include_examples "respect for private_email" include_examples "supports reply by email" include_examples "sets user locale" end @@ -554,6 +630,7 @@ describe UserNotifications do describe "user replied" do include_examples "notification email building" do let(:notification_type) { :replied } + include_examples "respect for private_email" include_examples "supports reply by email" include_examples "sets user locale" end @@ -562,6 +639,7 @@ describe UserNotifications do describe "user quoted" do include_examples "notification email building" do let(:notification_type) { :quoted } + include_examples "respect for private_email" include_examples "supports reply by email" include_examples "sets user locale" end @@ -570,6 +648,7 @@ describe UserNotifications do describe "user posted" do include_examples "notification email building" do let(:notification_type) { :posted } + include_examples "respect for private_email" include_examples "supports reply by email" include_examples "sets user locale" end @@ -578,6 +657,7 @@ describe UserNotifications do describe "user invited to a private message" do include_examples "notification email building" do let(:notification_type) { :invited_to_private_message } + include_examples "respect for private_email" include_examples "no reply by email" include_examples "sets user locale" end @@ -586,6 +666,7 @@ describe UserNotifications do describe "user invited to a topic" do include_examples "notification email building" do let(:notification_type) { :invited_to_topic } + include_examples "respect for private_email" include_examples "no reply by email" include_examples "sets user locale" end @@ -594,6 +675,7 @@ describe UserNotifications do describe "watching first post" do include_examples "notification email building" do let(:notification_type) { :invited_to_topic } + include_examples "respect for private_email" include_examples "no reply by email" include_examples "sets user locale" end diff --git a/spec/models/color_scheme_spec.rb b/spec/models/color_scheme_spec.rb index 34f75e4d94..4d07dac97d 100644 --- a/spec/models/color_scheme_spec.rb +++ b/spec/models/color_scheme_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' describe ColorScheme do - let(:valid_params) { {name: "Best Colors Evar", enabled: true, colors: valid_colors} } + let(:valid_params) { {name: "Best Colors Evar", colors: valid_colors} } let(:valid_colors) { [ {name: '$primary_background_color', hex: 'FFBB00'}, {name: '$secondary_background_color', hex: '888888'} @@ -10,7 +10,7 @@ describe ColorScheme do describe "new" do it "can take colors" do - c = described_class.new(valid_params) + c = ColorScheme.new(valid_params) expect(c.colors.size).to eq valid_colors.size expect(c.colors.first).to be_a(ColorSchemeColor) expect { @@ -55,29 +55,4 @@ describe ColorScheme do end end end - - describe "destroy" do - it "also destroys old versions" do - c1 = described_class.create(valid_params.merge(version: 2)) - _c2 = described_class.create(valid_params.merge(versioned_id: c1.id, version: 1)) - _other = described_class.create(valid_params) - expect { - c1.destroy - }.to change { described_class.count }.by(-2) - end - end - - describe "#enabled" do - it "returns nil when there is no enabled record" do - expect(described_class.enabled).to eq nil - end - - it "returns the enabled color scheme" do - ColorScheme.hex_cache.clear - expect(described_class.hex_for_name('$primary_background_color')).to eq nil - c = described_class.create(valid_params.merge(enabled: true)) - expect(described_class.enabled.id).to eq c.id - expect(described_class.hex_for_name('$primary_background_color')).to eq "FFBB00" - end - end end diff --git a/spec/models/group_user_spec.rb b/spec/models/group_user_spec.rb new file mode 100644 index 0000000000..c7675984cf --- /dev/null +++ b/spec/models/group_user_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +describe GroupUser do + + it 'correctly sets notification level' do + moderator = Fabricate(:moderator) + + Group.refresh_automatic_groups!(:moderators) + gu = GroupUser.find_by(user_id: moderator.id, group_id: Group::AUTO_GROUPS[:moderators]) + + expect(gu.notification_level).to eq(NotificationLevels.all[:tracking]) + + group = Group.create!(name: 'bob') + group.add(moderator) + group.save + + gu = GroupUser.find_by(user_id: moderator.id, group_id: group.id) + expect(gu.notification_level).to eq(NotificationLevels.all[:watching]) + + group.remove(moderator) + group.save + + group.default_notification_level = 1 + group.save + + group.add(moderator) + group.save + + gu = GroupUser.find_by(user_id: moderator.id, group_id: group.id) + expect(gu.notification_level).to eq(NotificationLevels.all[:regular]) + end + +end diff --git a/spec/models/invite_redeemer_spec.rb b/spec/models/invite_redeemer_spec.rb index 39d718508e..d1d93fda6b 100644 --- a/spec/models/invite_redeemer_spec.rb +++ b/spec/models/invite_redeemer_spec.rb @@ -43,7 +43,6 @@ describe InviteRedeemer do SiteSetting.must_approve_users = true inviter = invite.invited_by inviter.admin = true - Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_entries(username: username)) user = invite_redeemer.redeem expect(user.name).to eq(name) @@ -89,7 +88,6 @@ describe InviteRedeemer do it "can set password" do inviter = invite.invited_by - Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup)) user = InviteRedeemer.new(invite, username, name, password).redeem expect(user).to have_password expect(user.confirm_password?(password)).to eq(true) diff --git a/spec/models/invite_spec.rb b/spec/models/invite_spec.rb index b3784aa97b..7bba547df8 100644 --- a/spec/models/invite_spec.rb +++ b/spec/models/invite_spec.rb @@ -102,8 +102,15 @@ describe Invite do expect(topic.invite_by_email(inviter, 'ICEKING@adventuretime.ooo')).to eq(@invite) end + it 'updates timestamp of existing invite' do + @invite.created_at = 10.days.ago + @invite.save + resend_invite = topic.invite_by_email(inviter, 'iceking@adventuretime.ooo') + expect(resend_invite.created_at).to be_within(1.minute).of(Time.zone.now) + end + it 'returns a new invite if the other has expired' do - SiteSetting.stubs(:invite_expiry_days).returns(1) + SiteSetting.invite_expiry_days = 1 @invite.created_at = 2.days.ago @invite.save new_invite = topic.invite_by_email(inviter, 'iceking@adventuretime.ooo') @@ -206,34 +213,6 @@ describe Invite do end - context 'enqueues a job to email "set password" instructions' do - - it 'does not enqueue an email if sso is enabled' do - SiteSetting.stubs(:enable_sso).returns(true) - Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_key(:username)).never - invite.redeem - end - - it 'does not enqueue an email if local login is disabled' do - SiteSetting.stubs(:enable_local_logins).returns(false) - Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_key(:username)).never - invite.redeem - end - - it 'does not enqueue an email if the user has already set password' do - Fabricate(:user, email: invite.email, password_hash: "7af7805c9ee3697ed1a83d5e3cb5a3a431d140933a87fdcdc5a42aeef9337f81") - Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_key(:username)).never - Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup)) # should enqueue an account activation email - invite.redeem - end - - it 'enqueues an email if all conditions are satisfied' do - Jobs.expects(:enqueue).with(:invite_password_instructions_email, has_key(:username)) - invite.redeem - end - - end - context "as a moderator" do it "will give the user a moderator flag" do invite.invited_by = Fabricate(:admin) diff --git a/spec/models/remote_theme_spec.rb b/spec/models/remote_theme_spec.rb new file mode 100644 index 0000000000..16622e401e --- /dev/null +++ b/spec/models/remote_theme_spec.rb @@ -0,0 +1,112 @@ +require 'rails_helper' + +describe RemoteTheme do + context '#import_remote' do + def setup_git_repo(files) + dir = Dir.tmpdir + repo_dir = "#{dir}/#{SecureRandom.hex}" + `mkdir #{repo_dir}` + `cd #{repo_dir} && git init . ` + `cd #{repo_dir} && git config user.email 'someone@cool.com'` + `cd #{repo_dir} && git config user.name 'The Cool One'` + `cd #{repo_dir} && mkdir desktop mobile common` + files.each do |name, data| + File.write("#{repo_dir}/#{name}", data) + `cd #{repo_dir} && git add #{name}` + end + `cd #{repo_dir} && git commit -am 'first commit'` + repo_dir + end + + def about_json(options = {}) + options[:love] ||= "FAFAFA" + +< about_json, + "desktop/desktop.scss" => "body {color: red;}", + "common/header.html" => "I AM HEADER", + "common/random.html" => "I AM SILLY", + "common/embedded.scss" => "EMBED", + ) + end + + after do + `rm -fr #{initial_repo}` + end + + it 'can correctly import a remote theme' do + + time = Time.new('2000') + freeze_time time + + @theme = RemoteTheme.import_theme(initial_repo) + remote = @theme.remote_theme + + expect(@theme.name).to eq('awesome theme') + expect(remote.remote_url).to eq(initial_repo) + expect(remote.remote_version).to eq(`cd #{initial_repo} && git rev-parse HEAD`.strip) + expect(remote.local_version).to eq(`cd #{initial_repo} && git rev-parse HEAD`.strip) + + expect(remote.about_url).to eq("https://www.site.com/about") + expect(remote.license_url).to eq("https://www.site.com/license") + + expect(@theme.theme_fields.length).to eq(3) + + mapped = Hash[*@theme.theme_fields.map{|f| ["#{f.target}-#{f.name}", f.value]}.flatten] + + expect(mapped["0-header"]).to eq("I AM HEADER") + expect(mapped["1-scss"]).to eq("body {color: red;}") + expect(mapped["0-embedded_scss"]).to eq("EMBED") + + expect(remote.remote_updated_at).to eq(time) + + scheme = ColorScheme.find_by(theme_id: @theme.id) + expect(scheme.name).to eq("Amazing") + expect(scheme.colors.find_by(name: 'love').hex).to eq('fafafa') + + File.write("#{initial_repo}/common/header.html", "I AM UPDATED") + File.write("#{initial_repo}/about.json", about_json(love: "EAEAEA")) + + `cd #{initial_repo} && git commit -am "update"` + + + time = Time.new('2001') + freeze_time time + + remote.update_remote_version + expect(remote.commits_behind).to eq(1) + expect(remote.remote_version).to eq(`cd #{initial_repo} && git rev-parse HEAD`.strip) + + + remote.update_from_remote + @theme.save + @theme.reload + + scheme = ColorScheme.find_by(theme_id: @theme.id) + expect(scheme.name).to eq("Amazing") + expect(scheme.colors.find_by(name: 'love').hex).to eq('eaeaea') + + mapped = Hash[*@theme.theme_fields.map{|f| ["#{f.target}-#{f.name}", f.value]}.flatten] + + expect(mapped["0-header"]).to eq("I AM UPDATED") + expect(mapped["1-scss"]).to eq("body {color: red;}") + expect(remote.remote_updated_at).to eq(time) + + end + end +end diff --git a/spec/models/s3_region_site_setting_spec.rb b/spec/models/s3_region_site_setting_spec.rb index fb8b203350..9f36a09e28 100644 --- a/spec/models/s3_region_site_setting_spec.rb +++ b/spec/models/s3_region_site_setting_spec.rb @@ -14,7 +14,7 @@ describe S3RegionSiteSetting do describe 'values' do it 'returns all the S3 regions' do - expect(S3RegionSiteSetting.values.map {|x| x[:value]}.sort).to eq(['us-east-1', 'us-west-1', 'us-west-2', 'us-gov-west-1', 'eu-west-1', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-south-1', 'ap-northeast-1', 'ap-northeast-2', 'sa-east-1', 'cn-north-1'].sort) + expect(S3RegionSiteSetting.values.map {|x| x[:value]}.sort).to eq(['us-east-1', 'us-west-1', 'us-west-2', 'us-gov-west-1', 'eu-west-1', 'eu-west-2', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-south-1', 'ap-northeast-1', 'ap-northeast-2', 'sa-east-1', 'cn-north-1'].sort) end end diff --git a/spec/models/site_customization_spec.rb b/spec/models/site_customization_spec.rb deleted file mode 100644 index e762c78d54..0000000000 --- a/spec/models/site_customization_spec.rb +++ /dev/null @@ -1,155 +0,0 @@ -require 'rails_helper' - -describe SiteCustomization do - - before do - SiteCustomization.clear_cache! - end - - let :user do - Fabricate(:user) - end - - let :customization_params do - {name: 'my name', user_id: user.id, header: "my awesome header", stylesheet: "my awesome css", mobile_stylesheet: nil, mobile_header: nil} - end - - let :customization do - SiteCustomization.create!(customization_params) - end - - let :customization_with_mobile do - SiteCustomization.create!(customization_params.merge(mobile_stylesheet: ".mobile {better: true;}", mobile_header: "fancy mobile stuff")) - end - - it 'should set default key when creating a new customization' do - s = SiteCustomization.create!(name: 'my name', user_id: user.id) - expect(s.key).not_to eq(nil) - end - - it 'can enable more than one style at once' do - c1 = SiteCustomization.create!(name: '2', user_id: user.id, header: 'World', - enabled: true, mobile_header: 'hi', footer: 'footer', - stylesheet: '.hello{.world {color: blue;}}') - - SiteCustomization.create!(name: '1', user_id: user.id, header: 'Hello', - enabled: true, mobile_footer: 'mfooter', - mobile_stylesheet: '.hello{margin: 1px;}', - stylesheet: 'p{width: 1px;}' - ) - - expect(SiteCustomization.custom_header).to eq("Hello\nWorld") - expect(SiteCustomization.custom_header(nil, :mobile)).to eq("hi") - expect(SiteCustomization.custom_footer(nil, :mobile)).to eq("mfooter") - expect(SiteCustomization.custom_footer).to eq("footer") - - desktop_css = SiteCustomization.custom_stylesheet - expect(desktop_css).to match(Regexp.new("#{SiteCustomization::ENABLED_KEY}.css\\?target=desktop")) - - mobile_css = SiteCustomization.custom_stylesheet(nil, :mobile) - expect(mobile_css).to match(Regexp.new("#{SiteCustomization::ENABLED_KEY}.css\\?target=mobile")) - - expect(SiteCustomization.enabled_stylesheet_contents).to match(/\.hello \.world/) - - # cache expiry - c1.enabled = false - c1.save - - expect(SiteCustomization.custom_stylesheet).not_to eq(desktop_css) - expect(SiteCustomization.enabled_stylesheet_contents).not_to match(/\.hello \.world/) - end - - it 'should be able to look up stylesheets by key' do - c = SiteCustomization.create!(name: '2', user_id: user.id, - enabled: true, - stylesheet: '.hello{.world {color: blue;}}', - mobile_stylesheet: '.world{.hello{color: black;}}') - - expect(SiteCustomization.custom_stylesheet(c.key, :mobile)).to match(Regexp.new("#{c.key}.css\\?target=mobile")) - expect(SiteCustomization.custom_stylesheet(c.key)).to match(Regexp.new("#{c.key}.css\\?target=desktop")) - - end - - - it 'should allow including discourse styles' do - c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '@import "desktop";', mobile_stylesheet: '@import "mobile";') - expect(c.stylesheet_baked).not_to match(/Syntax error/) - expect(c.stylesheet_baked.length).to be > 1000 - expect(c.mobile_stylesheet_baked).not_to match(/Syntax error/) - expect(c.mobile_stylesheet_baked.length).to be > 1000 - end - - it 'should provide an awesome error on failure' do - c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", header: '') - expect(c.stylesheet_baked).to match(/Syntax error/) - expect(c.mobile_stylesheet_baked).not_to be_present - end - - it 'should provide an awesome error on failure for mobile too' do - c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '', header: '', mobile_stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", mobile_header: '') - expect(c.mobile_stylesheet_baked).to match(/Syntax error/) - expect(c.stylesheet_baked).not_to be_present - end - - it 'should correct bad html in body_tag_baked and head_tag_baked' do - c = SiteCustomization.create!(user_id: -1, name: "test", head_tag: "I am bold", body_tag: "I am bold") - expect(c.head_tag_baked).to eq("I am bold") - expect(c.body_tag_baked).to eq("I am bold") - end - - it 'should precompile fragments in body and head tags' do - with_template = < - {{hello}} - - -HTML - c = SiteCustomization.create!(user_id: -1, name: "test", head_tag: with_template, body_tag: with_template) - expect(c.head_tag_baked).to match(/HTMLBars/) - expect(c.body_tag_baked).to match(/HTMLBars/) - expect(c.body_tag_baked).to match(/raw-handlebars/) - expect(c.head_tag_baked).to match(/raw-handlebars/) - end - - it 'should create body_tag_baked on demand if needed' do - c = SiteCustomization.create!(user_id: -1, name: "test", head_tag: "test", enabled: true) - c.update_columns(head_tag_baked: nil) - expect(SiteCustomization.custom_head_tag).to match(/test<\/b>/) - end - - context "plugin api" do - def transpile(html) - c = SiteCustomization.create!(user_id: -1, name: "test", head_tag: html, body_tag: html) - c.head_tag_baked - end - - it "transpiles ES6 code" do - html = < - const x = 1; - -HTML - - transpiled = transpile(html) - expect(transpiled).to match(/\/) - expect(transpiled).to match(/var x = 1;/) - expect(transpiled).to match(/_registerPluginCode\('0.1'/) - end - - it "converts errors to a script type that is not evaluated" do - html = < - const x = 1; - x = 2; - -HTML - - transpiled = transpile(html) - expect(transpiled).to match(/text\/discourse-js-error/) - expect(transpiled).to match(/read-only/) - end - end - -end diff --git a/spec/models/site_spec.rb b/spec/models/site_spec.rb index d8bb189b34..8899269c80 100644 --- a/spec/models/site_spec.rb +++ b/spec/models/site_spec.rb @@ -2,6 +2,44 @@ require 'rails_helper' require_dependency 'site' describe Site do + + def expect_correct_themes(guardian) + json = Site.json_for(guardian) + parsed = JSON.parse(json) + + expected = Theme.where('key = :default OR user_selectable', + default: SiteSetting.default_theme_key) + .order(:name) + .pluck(:key, :name) + .map{|k,n| {"theme_key" => k, "name" => n, "default" => k == SiteSetting.default_theme_key}} + + expect(parsed["user_themes"]).to eq(expected) + end + + it "includes user themes and expires them as needed" do + default_theme = Theme.create!(user_id: -1, name: 'default') + SiteSetting.default_theme_key = default_theme.key + user_theme = Theme.create!(user_id: -1, name: 'user theme', user_selectable: true) + + anon_guardian = Guardian.new + user_guardian = Guardian.new(Fabricate(:user)) + + expect_correct_themes(anon_guardian) + expect_correct_themes(user_guardian) + + Theme.clear_default! + + expect_correct_themes(anon_guardian) + expect_correct_themes(user_guardian) + + user_theme.user_selectable = false + user_theme.save! + + expect_correct_themes(anon_guardian) + expect_correct_themes(user_guardian) + + end + it "omits categories users can not write to from the category list" do category = Fabricate(:category) user = Fabricate(:user) diff --git a/spec/models/stylesheet_cache_spec.rb b/spec/models/stylesheet_cache_spec.rb index eb52d07bcc..2c49fc36ba 100644 --- a/spec/models/stylesheet_cache_spec.rb +++ b/spec/models/stylesheet_cache_spec.rb @@ -5,7 +5,7 @@ describe StylesheetCache do describe "add" do it "correctly cycles once MAX_TO_KEEP is hit" do (StylesheetCache::MAX_TO_KEEP + 1).times do |i| - StylesheetCache.add("a", "d" + i.to_s, "c" + i.to_s) + StylesheetCache.add("a", "d" + i.to_s, "c" + i.to_s, "map") end expect(StylesheetCache.count).to eq StylesheetCache::MAX_TO_KEEP @@ -13,8 +13,8 @@ describe StylesheetCache do end it "does nothing if digest is set and already exists" do - StylesheetCache.add("a", "b", "c") - StylesheetCache.add("a", "b", "cc") + StylesheetCache.add("a", "b", "c", "map") + StylesheetCache.add("a", "b", "cc", "map") expect(StylesheetCache.count).to eq 1 expect(StylesheetCache.first.content).to eq "c" diff --git a/spec/models/theme_field_spec.rb b/spec/models/theme_field_spec.rb new file mode 100644 index 0000000000..f120526e1c --- /dev/null +++ b/spec/models/theme_field_spec.rb @@ -0,0 +1,30 @@ +# encoding: utf-8 + +require 'rails_helper' + +describe ThemeField do + it "correctly generates errors for transpiled js" do + html = < + badJavaScript(; + +HTML + field = ThemeField.create!(theme_id: 1, target: 0, name: "header", value: html) + expect(field.error).not_to eq(nil) + field.value = "" + field.save! + expect(field.error).to eq(nil) + end + + it "correctly generates errors for transpiled css" do + css = "body {" + field = ThemeField.create!(theme_id: 1, target: 0, name: "scss", value: css) + field.reload + expect(field.error).not_to eq(nil) + field.value = "body {color: blue};" + field.save! + field.reload + + expect(field.error).to eq(nil) + end +end diff --git a/spec/models/theme_spec.rb b/spec/models/theme_spec.rb new file mode 100644 index 0000000000..e75bdb5d8a --- /dev/null +++ b/spec/models/theme_spec.rb @@ -0,0 +1,164 @@ +require 'rails_helper' + +describe Theme do + + before do + Theme.clear_cache! + end + + let :user do + Fabricate(:user) + end + + let :customization_params do + {name: 'my name', user_id: user.id, header: "my awesome header"} + end + + let :customization do + Theme.create!(customization_params) + end + + it 'should set default key when creating a new customization' do + s = Theme.create!(name: 'my name', user_id: user.id) + expect(s.key).not_to eq(nil) + end + + it 'can support child themes' do + child = Theme.new(name: '2', user_id: user.id) + + child.set_field(:common, "header", "World") + child.set_field(:desktop, "header", "Desktop") + child.set_field(:mobile, "header", "Mobile") + + child.save! + + expect(Theme.lookup_field(child.key, :desktop, "header")).to eq("World\nDesktop") + expect(Theme.lookup_field(child.key, "mobile", :header)).to eq("World\nMobile") + + + child.set_field(:common, "header", "Worldie") + child.save! + + expect(Theme.lookup_field(child.key, :mobile, :header)).to eq("Worldie\nMobile") + + parent = Theme.new(name: '1', user_id: user.id) + + parent.set_field(:common, "header", "Common Parent") + parent.set_field(:mobile, "header", "Mobile Parent") + + parent.save! + + parent.add_child_theme!(child) + + expect(Theme.lookup_field(parent.key, :mobile, "header")).to eq("Common Parent\nMobile Parent\nWorldie\nMobile") + + end + + it 'can correctly find parent themes' do + grandchild = Theme.create!(name: 'grandchild', user_id: user.id) + child = Theme.create!(name: 'child', user_id: user.id) + theme = Theme.create!(name: 'theme', user_id: user.id) + + theme.add_child_theme!(child) + child.add_child_theme!(grandchild) + + expect(grandchild.dependant_themes.length).to eq(2) + end + + + it 'should correct bad html in body_tag_baked and head_tag_baked' do + theme = Theme.new(user_id: -1, name: "test") + theme.set_field(:common, "head_tag", "I am bold") + theme.save! + + expect(Theme.lookup_field(theme.key, :desktop, "head_tag")).to eq("I am bold") + end + + it 'should precompile fragments in body and head tags' do + with_template = < + {{hello}} + + +HTML + theme = Theme.new(user_id: -1, name: "test") + theme.set_field(:common, "header", with_template) + theme.save! + + baked = Theme.lookup_field(theme.key, :mobile, "header") + + expect(baked).to match(/HTMLBars/) + expect(baked).to match(/raw-handlebars/) + end + + it 'should create body_tag_baked on demand if needed' do + + theme = Theme.new(user_id: -1, name: "test") + theme.set_field(:common, :body_tag, "test") + theme.save + + ThemeField.update_all(value_baked: nil) + + expect(Theme.lookup_field(theme.key, :desktop, :body_tag)).to match(/test<\/b>/) + end + + context "plugin api" do + def transpile(html) + f = ThemeField.create!(target: Theme.targets[:mobile], theme_id: -1, name: "after_header", value: html) + f.value_baked + end + + it "transpiles ES6 code" do + html = < + const x = 1; + +HTML + + transpiled = transpile(html) + expect(transpiled).to match(/\/) + expect(transpiled).to match(/var x = 1;/) + expect(transpiled).to match(/_registerPluginCode\('0.1'/) + end + + it "converts errors to a script type that is not evaluated" do + html = < + const x = 1; + x = 2; + +HTML + + transpiled = transpile(html) + expect(transpiled).to match(/text\/discourse-js-error/) + expect(transpiled).to match(/read-only/) + end + end + + it 'correctly caches theme keys' do + theme = Theme.create!(name: "bob", user_id: -1) + + expect(Theme.theme_keys).to eq(Set.new([theme.key])) + expect(Theme.user_theme_keys).to eq(Set.new([])) + + theme.user_selectable = true + theme.save + + expect(Theme.user_theme_keys).to eq(Set.new([theme.key])) + + theme.user_selectable = false + theme.save + + theme.set_default! + expect(Theme.user_theme_keys).to eq(Set.new([theme.key])) + + theme.destroy + + expect(Theme.theme_keys).to eq(Set.new([])) + expect(Theme.user_theme_keys).to eq(Set.new([])) + end + + +end diff --git a/spec/models/topic_converter_spec.rb b/spec/models/topic_converter_spec.rb index 96bd61e915..cee779633b 100644 --- a/spec/models/topic_converter_spec.rb +++ b/spec/models/topic_converter_spec.rb @@ -5,13 +5,42 @@ describe TopicConverter do context 'convert_to_public_topic' do let(:admin) { Fabricate(:admin) } let(:author) { Fabricate(:user) } + let(:category) { Fabricate(:category) } let(:private_message) { Fabricate(:private_message_topic, user: author) } context 'success' do it "converts private message to regular topic" do - topic = private_message.convert_to_public_topic(admin) + SiteSetting.allow_uncategorized_topics = true + topic = described_class.new(private_message, admin).convert_to_public_topic + topic.reload + expect(topic).to be_valid expect(topic.archetype).to eq("regular") + expect(topic.category_id).to eq(SiteSetting.uncategorized_category_id) + end + + describe 'when uncategorized category is not allowed' do + before do + SiteSetting.allow_uncategorized_topics = false + category.update!(read_restricted: false) + end + + it 'should convert private message into the right category' do + topic = described_class.new(private_message, admin).convert_to_public_topic + topic.reload + + expect(topic).to be_valid + expect(topic.archetype).to eq("regular") + expect(topic.category_id).to eq(category.id) + end + end + + describe 'when a custom category_id is given' do + it 'should convert private message into the right category' do + topic = described_class.new(private_message, admin).convert_to_public_topic(category.id) + + expect(topic.reload.category).to eq(category) + end end it "updates user stats" do diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 8fe1d46d0b..9db33f78bd 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -1493,6 +1493,15 @@ describe Topic do expect { topic.trash!(moderator) }.to_not change { category.reload.topic_count } end end + + it "trashes topic embed record" do + topic = Fabricate(:topic) + post = Fabricate(:post, topic: topic, post_number: 1) + topic_embed = TopicEmbed.create!(topic_id: topic.id, embed_url: "https://blog.codinghorror.com/password-rules-are-bullshit", post_id: post.id) + topic.trash! + topic_embed.reload + expect(topic_embed.deleted_at).not_to eq(nil) + end end describe 'recover!' do @@ -1509,6 +1518,15 @@ describe Topic do expect { topic.recover! }.to_not change { category.reload.topic_count } end end + + it "recovers topic embed record" do + topic = Fabricate(:topic, deleted_at: 1.day.ago) + post = Fabricate(:post, topic: topic, post_number: 1) + topic_embed = TopicEmbed.create!(topic_id: topic.id, embed_url: "https://blog.codinghorror.com/password-rules-are-bullshit", post_id: post.id, deleted_at: 1.day.ago) + topic.recover! + topic_embed.reload + expect(topic_embed.deleted_at).to eq(nil) + end end context "new user limits" do @@ -1849,4 +1867,54 @@ describe Topic do expect(Topic.with_no_response_total).to eq(1) end end + + describe '#pm_with_non_human_user?' do + let(:robot) { Fabricate(:user, id: -3) } + let(:user) { Fabricate(:user) } + + let(:topic) do + Fabricate(:private_message_topic, topic_allowed_users: [ + Fabricate.build(:topic_allowed_user, user: robot), + Fabricate.build(:topic_allowed_user, user: user) + ]) + end + + describe 'when PM is between a human and a non human user' do + it 'should return true' do + expect(topic.pm_with_non_human_user?).to be(true) + end + end + + describe 'when PM contains 2 human users and a non human user' do + it 'should return false' do + Fabricate(:topic_allowed_user, topic: topic, user: Fabricate(:user)) + + expect(topic.pm_with_non_human_user?).to be(false) + end + end + + describe 'when PM only contains a user' do + it 'should return true' do + topic.topic_allowed_users.first.destroy! + + expect(topic.reload.pm_with_non_human_user?).to be(true) + end + end + + describe 'when PM contains a group' do + it 'should return false' do + Fabricate(:topic_allowed_group, topic: topic) + + expect(topic.pm_with_non_human_user?).to be(false) + end + end + + describe 'when topic is not a PM' do + it 'should return false' do + topic.convert_to_public_topic(Fabricate(:admin)) + + expect(topic.pm_with_non_human_user?).to be(false) + end + end + end end diff --git a/spec/models/topic_status_update_spec.rb b/spec/models/topic_status_update_spec.rb index 6880ddc2ec..7b246b8819 100644 --- a/spec/models/topic_status_update_spec.rb +++ b/spec/models/topic_status_update_spec.rb @@ -226,6 +226,11 @@ RSpec.describe TopicStatusUpdate, type: :model do Fabricate(:topic_status_update) + Fabricate(:topic_status_update, + execute_at: Time.zone.now - 1.hour, + created_at: Time.zone.now - 2.hour + ).topic.trash! + expect { described_class.ensure_consistency! } .to change { Jobs::ToggleTopicClosed.jobs.count }.by(2) diff --git a/spec/models/topic_user_spec.rb b/spec/models/topic_user_spec.rb index 6b8cc4b889..56e7472ebe 100644 --- a/spec/models/topic_user_spec.rb +++ b/spec/models/topic_user_spec.rb @@ -72,7 +72,7 @@ describe TopicUser do guardian = Guardian.new(u) TopicCreator.create(u, guardian, title: "this is my topic title") } - let(:topic_user) { TopicUser.get(topic,user) } + let(:topic_user) { TopicUser.get(topic, user) } let(:topic_creator_user) { TopicUser.get(topic, topic.user) } let(:post) { Fabricate(:post, topic: topic, user: user) } @@ -99,6 +99,18 @@ describe TopicUser do end describe 'notifications' do + it 'should trigger the right DiscourseEvent' do + begin + called = false + DiscourseEvent.on(:topic_notification_level_changed) { called = true } + + TopicUser.change(user.id, topic.id, notification_level: TopicUser.notification_levels[:tracking]) + + expect(called).to eq(true) + ensure + DiscourseEvent.off(:topic_notification_level_changed) { called = true } + end + end it 'should be set to tracking if auto_track_topics is enabled' do user.user_option.update_column(:auto_track_topics_after_msecs, 0) @@ -238,6 +250,50 @@ describe TopicUser do expect(topic_new_user.notifications_reason_id).to eq(TopicUser.notification_reasons[:created_post]) end + it 'should update tracking state when you reply' do + new_user.user_option.update_column(:notification_level_when_replying, 3) + post_creator.create + TopicUser.exec_sql("UPDATE topic_users set notification_level=2 + WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) + TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:watching]) + + tu = TopicUser.find_by(user_id: topic_new_user.user_id, topic_id: topic_new_user.topic_id) + expect(tu.notification_level).to eq(TopicUser.notification_levels[:watching]) + end + + it 'should not update tracking state when you reply' do + new_user.user_option.update_column(:notification_level_when_replying, 3) + post_creator.create + TopicUser.exec_sql("UPDATE topic_users set notification_level=3 + WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) + TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:tracking]) + + tu = TopicUser.find_by(user_id: topic_new_user.user_id, topic_id: topic_new_user.topic_id) + expect(tu.notification_level).to eq(TopicUser.notification_levels[:watching]) + end + + it 'should not update tracking state when state manually set to normal you reply' do + new_user.user_option.update_column(:notification_level_when_replying, 3) + post_creator.create + TopicUser.exec_sql("UPDATE topic_users set notification_level=1 + WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) + TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:tracking]) + + tu = TopicUser.find_by(user_id: topic_new_user.user_id, topic_id: topic_new_user.topic_id) + expect(tu.notification_level).to eq(TopicUser.notification_levels[:regular]) + end + + it 'should not update tracking state when state manually set to muted you reply' do + new_user.user_option.update_column(:notification_level_when_replying, 3) + post_creator.create + TopicUser.exec_sql("UPDATE topic_users set notification_level=0 + WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) + TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:tracking]) + + tu = TopicUser.find_by(user_id: topic_new_user.user_id, topic_id: topic_new_user.topic_id) + expect(tu.notification_level).to eq(TopicUser.notification_levels[:muted]) + end + it 'should not automatically track topics you reply to and have set state manually' do post_creator.create TopicUser.change(new_user, topic, notification_level: TopicUser.notification_levels[:regular]) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 9304b1279a..8100e92782 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -459,7 +459,7 @@ describe User do end end - context '.username_available?' do + describe '.username_available?' do it "returns true for a username that is available" do expect(User.username_available?('BruceWayne')).to eq(true) end @@ -471,25 +471,33 @@ describe User do it 'returns false when a username is reserved' do SiteSetting.reserved_usernames = 'test|donkey' - expect(User.username_available?('donkey')).to eq(false) - expect(User.username_available?('DonKey')).to eq(false) - expect(User.username_available?('test')).to eq(false) + expect(User.username_available?('tESt')).to eq(false) + end + end + + describe '.reserved_username?' do + it 'returns true when a username is reserved' do + SiteSetting.reserved_usernames = 'test|donkey' + + expect(User.reserved_username?('donkey')).to eq(true) + expect(User.reserved_username?('DonKey')).to eq(true) + expect(User.reserved_username?('test')).to eq(true) end it 'should not allow usernames matched against an expession' do SiteSetting.reserved_usernames = 'test)|*admin*|foo*|*bar|abc.def' - expect(User.username_available?('test')).to eq(true) - expect(User.username_available?('abc9def')).to eq(true) + expect(User.reserved_username?('test')).to eq(false) + expect(User.reserved_username?('abc9def')).to eq(false) - expect(User.username_available?('admin')).to eq(false) - expect(User.username_available?('foo')).to eq(false) - expect(User.username_available?('bar')).to eq(false) + expect(User.reserved_username?('admin')).to eq(true) + expect(User.reserved_username?('foo')).to eq(true) + expect(User.reserved_username?('bar')).to eq(true) - expect(User.username_available?('admi')).to eq(true) - expect(User.username_available?('bar.foo')).to eq(true) - expect(User.username_available?('foo.bar')).to eq(false) - expect(User.username_available?('baz.bar')).to eq(false) + expect(User.reserved_username?('admi')).to eq(false) + expect(User.reserved_username?('bar.foo')).to eq(false) + expect(User.reserved_username?('foo.bar')).to eq(true) + expect(User.reserved_username?('baz.bar')).to eq(true) end end @@ -1147,6 +1155,7 @@ describe User do describe "refresh_avatar" do it "enqueues the update_gravatar job when automatically downloading gravatars" do SiteSetting.automatically_download_gravatars = true + SiteSetting.queue_jobs = true user = Fabricate(:user) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index cc2ac9b91b..985bea4fb6 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -9,17 +9,14 @@ require 'rbtrace' #uncomment the following line to use spork with the debugger #require 'spork/ext/ruby-debug' -require 'fakeweb' -FakeWeb.allow_net_connect = false - Spork.prefork do # Loading more in this block will cause your tests to run faster. However, # if you change any configuration or code from libraries loaded here, you'll # need to restart spork for it take effect. require 'fabrication' require 'mocha/api' - require 'fakeweb' require 'certified' + require 'webmock/rspec' ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) diff --git a/spec/serializers/web_hook_post_serializer_spec.rb b/spec/serializers/web_hook_post_serializer_spec.rb new file mode 100644 index 0000000000..2f1f487060 --- /dev/null +++ b/spec/serializers/web_hook_post_serializer_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +RSpec.describe WebHookPostSerializer do + let(:admin) { Fabricate(:admin) } + let(:post) { Fabricate(:post) } + let(:serializer) { WebHookPostSerializer.new(post, scope: Guardian.new(admin), root: false) } + + it 'should only include the required keys' do + count = serializer.as_json.keys.count + difference = count - 40 + + expect(difference).to eq(0), lambda { + message = "" + + if difference < 0 + message << "#{difference * -1} key(s) have been removed from this serializer." + else + message << "#{difference} key(s) have been added to this serializer." + end + + message << "\nPlease verify if those key(s) are required as part of the web hook's payload." + } + end +end diff --git a/spec/services/color_scheme_revisor_spec.rb b/spec/services/color_scheme_revisor_spec.rb index d9f8a3519a..c2dc09d2c7 100644 --- a/spec/services/color_scheme_revisor_spec.rb +++ b/spec/services/color_scheme_revisor_spec.rb @@ -3,62 +3,42 @@ require 'rails_helper' describe ColorSchemeRevisor do let(:color) { Fabricate.build(:color_scheme_color, hex: 'FFFFFF', color_scheme: nil) } - let(:color_scheme) { Fabricate(:color_scheme, enabled: false, created_at: 1.day.ago, updated_at: 1.day.ago, color_scheme_colors: [color]) } - let(:valid_params) { { name: color_scheme.name, enabled: color_scheme.enabled, colors: nil } } + let(:color_scheme) { Fabricate(:color_scheme, created_at: 1.day.ago, updated_at: 1.day.ago, color_scheme_colors: [color]) } + let(:valid_params) { { name: color_scheme.name, colors: nil } } describe "revise" do it "does nothing if there are no changes" do expect { - described_class.revise(color_scheme, valid_params.merge(colors: nil)) + ColorSchemeRevisor.revise(color_scheme, valid_params.merge(colors: nil)) }.to_not change { color_scheme.reload.updated_at } end it "can change the name" do - described_class.revise(color_scheme, valid_params.merge(name: "Changed Name")) + ColorSchemeRevisor.revise(color_scheme, valid_params.merge(name: "Changed Name")) expect(color_scheme.reload.name).to eq("Changed Name") end - it "can update the theme_id" do - described_class.revise(color_scheme, valid_params.merge(theme_id: 'test')) - expect(color_scheme.reload.theme_id).to eq('test') + it "can update the base_scheme_id" do + ColorSchemeRevisor.revise(color_scheme, valid_params.merge(base_scheme_id: 'test')) + expect(color_scheme.reload.base_scheme_id).to eq('test') end - it "can enable and disable" do - described_class.revise(color_scheme, valid_params.merge(enabled: true)) - expect(color_scheme.reload).to be_enabled - described_class.revise(color_scheme, valid_params.merge(enabled: false)) - expect(color_scheme.reload).not_to be_enabled - end - - def test_color_change(color_scheme_arg, expected_enabled) - described_class.revise(color_scheme_arg, valid_params.merge(colors: [ - {name: color.name, hex: 'BEEF99'} + it 'can change colors' do + ColorSchemeRevisor.revise(color_scheme, valid_params.merge(colors: [ + {name: color.name, hex: 'BEEF99'}, + {name: 'bob', hex: 'AAAAAA'} ])) - color_scheme_arg.reload - expect(color_scheme_arg.enabled).to eq(expected_enabled) - expect(color_scheme_arg.colors.size).to eq(1) - expect(color_scheme_arg.colors.first.hex).to eq('BEEF99') - end + color_scheme.reload - it "can change colors of a color scheme that's not enabled" do - test_color_change(color_scheme, false) - end - - it "can change colors of the enabled color scheme" do - color_scheme.update_attribute(:enabled, true) - test_color_change(color_scheme, true) - end - - it "disables other color scheme before enabling" do - prev_enabled = Fabricate(:color_scheme, enabled: true) - described_class.revise(color_scheme, valid_params.merge(enabled: true)) - expect(prev_enabled.reload.enabled).to eq(false) - expect(color_scheme.reload.enabled).to eq(true) + expect(color_scheme.version).to eq(2) + expect(color_scheme.colors.size).to eq(2) + expect(color_scheme.colors.find_by(name: color.name).hex).to eq('BEEF99') + expect(color_scheme.colors.find_by(name: 'bob').hex).to eq('AAAAAA') end it "doesn't make changes when a color is invalid" do expect { - cs = described_class.revise(color_scheme, valid_params.merge(colors: [ + cs = ColorSchemeRevisor.revise(color_scheme, valid_params.merge(colors: [ {name: color.name, hex: 'OOPS'} ])) expect(cs).not_to be_valid @@ -66,72 +46,6 @@ describe ColorSchemeRevisor do }.to_not change { color_scheme.reload.version } expect(color_scheme.colors.first.hex).to eq(color.hex) end - - describe "versions" do - it "doesn't create a new version if colors is not given" do - expect { - described_class.revise(color_scheme, valid_params.merge(name: "Changed Name")) - }.to_not change { color_scheme.reload.version } - end - - it "creates a new version if colors have changed" do - old_hex = color.hex - expect { - described_class.revise(color_scheme, valid_params.merge(colors: [ - {name: color.name, hex: 'BEEF99'} - ])) - }.to change { color_scheme.reload.version }.by(1) - old_version = ColorScheme.find_by(versioned_id: color_scheme.id, version: (color_scheme.version - 1)) - expect(old_version).not_to eq(nil) - expect(old_version.colors.count).to eq(color_scheme.colors.count) - expect(old_version.colors_by_name[color.name].hex).to eq(old_hex) - expect(color_scheme.colors_by_name[color.name].hex).to eq('BEEF99') - end - - it "doesn't create a new version if colors have not changed" do - expect { - described_class.revise(color_scheme, valid_params.merge(colors: [ - {name: color.name, hex: color.hex} - ])) - }.to_not change { color_scheme.reload.version } - end - end - end - - describe "revert" do - context "when there are no previous versions" do - it "does nothing" do - expect { - expect(described_class.revert(color_scheme)).to eq(color_scheme) - }.to_not change { color_scheme.reload.version } - end - end - - context 'when there are previous versions' do - let(:new_color_params) { {name: color.name, hex: 'BEEF99'} } - - before do - @prev_hex = color.hex - described_class.revise(color_scheme, valid_params.merge(colors: [ new_color_params ])) - end - - it "reverts the colors to the previous version" do - expect(color_scheme.colors_by_name[new_color_params[:name]].hex).to eq(new_color_params[:hex]) - expect { - described_class.revert(color_scheme) - }.to change { color_scheme.reload.version }.by(-1) - expect(color_scheme.colors.size).to eq(1) - expect(color_scheme.colors.first.hex).to eq(@prev_hex) - expect(color_scheme.colors_by_name[new_color_params[:name]].hex).to eq(@prev_hex) - end - - it "destroys the old version's record" do - expect { - described_class.revert(color_scheme) - }.to change { ColorScheme.count }.by(-1) - expect(color_scheme.reload.previous_version).to eq(nil) - end - end end end diff --git a/spec/services/staff_action_logger_spec.rb b/spec/services/staff_action_logger_spec.rb index 26d7402c2a..2be67fbfbf 100644 --- a/spec/services/staff_action_logger_spec.rb +++ b/spec/services/staff_action_logger_spec.rb @@ -129,46 +129,56 @@ describe StaffActionLogger do end end - describe "log_site_customization_change" do - let(:valid_params) { {name: 'Cool Theme', stylesheet: "body {\n background-color: blue;\n}\n", header: "h1 {color: white;}"} } + describe "log_theme_change" do it "raises an error when params are invalid" do - expect { logger.log_site_customization_change(nil, nil) }.to raise_error(Discourse::InvalidParameters) + expect { logger.log_theme_change(nil, nil) }.to raise_error(Discourse::InvalidParameters) + end + + let :theme do + Theme.new(name: 'bob', user_id: -1) end it "logs new site customizations" do - log_record = logger.log_site_customization_change(nil, valid_params) - expect(log_record.subject).to eq(valid_params[:name]) + + log_record = logger.log_theme_change(nil, theme) + expect(log_record.subject).to eq(theme.name) expect(log_record.previous_value).to eq(nil) expect(log_record.new_value).to be_present + json = ::JSON.parse(log_record.new_value) - expect(json['stylesheet']).to be_present - expect(json['header']).to be_present + expect(json['name']).to eq(theme.name) end it "logs updated site customizations" do - existing = SiteCustomization.new(name: 'Banana', stylesheet: "body {color: yellow;}", header: "h1 {color: brown;}") - log_record = logger.log_site_customization_change(existing, valid_params) + old_json = ThemeSerializer.new(theme, root:false).to_json + + theme.set_field(:common, :scss, "body{margin: 10px;}") + + log_record = logger.log_theme_change(old_json, theme) + expect(log_record.previous_value).to be_present - json = ::JSON.parse(log_record.previous_value) - expect(json['stylesheet']).to eq(existing.stylesheet) - expect(json['header']).to eq(existing.header) + + json = ::JSON.parse(log_record.new_value) + expect(json['theme_fields']).to eq([{"name" => "scss", "target" => "common", "value" => "body{margin: 10px;}"}]) end end - describe "log_site_customization_destroy" do + describe "log_theme_destroy" do it "raises an error when params are invalid" do - expect { logger.log_site_customization_destroy(nil) }.to raise_error(Discourse::InvalidParameters) + expect { logger.log_theme_destroy(nil) }.to raise_error(Discourse::InvalidParameters) end it "creates a new UserHistory record" do - site_customization = SiteCustomization.new(name: 'Banana', stylesheet: "body {color: yellow;}", header: "h1 {color: brown;}") - log_record = logger.log_site_customization_destroy(site_customization) + theme = Theme.new(name: 'Banana') + theme.set_field(:common, :scss, "body{margin: 10px;}") + + log_record = logger.log_theme_destroy(theme) expect(log_record.previous_value).to be_present expect(log_record.new_value).to eq(nil) json = ::JSON.parse(log_record.previous_value) - expect(json['stylesheet']).to eq(site_customization.stylesheet) - expect(json['header']).to eq(site_customization.header) + + expect(json['theme_fields']).to eq([{"name" => "scss", "target" => "common", "value" => "body{margin: 10px;}"}]) end end diff --git a/test/javascripts/acceptance/topic-notifications-button-test.js.es6 b/test/javascripts/acceptance/topic-notifications-button-test.js.es6 new file mode 100644 index 0000000000..4689b86bb0 --- /dev/null +++ b/test/javascripts/acceptance/topic-notifications-button-test.js.es6 @@ -0,0 +1,47 @@ +import { acceptance } from "helpers/qunit-helpers"; +acceptance("Topic Notifications button", { + loggedIn: true, + setup() { + const response = object => { + return [ + 200, + { "Content-Type": "application/json" }, + object + ]; + }; + + server.post('/t/280/notifications', () => { // eslint-disable-line no-undef + return response({}); + }); + } +}); + +test("Updating topic notification level", () => { + visit("/t/internationalization-localization/280"); + + const notificationOptions = "#topic-footer-buttons .notification-options"; + + andThen(() => { + ok( + exists(`${notificationOptions} .tracking`), + "it should display the notification options button in the topic's footer" + ); + }); + + click(`${notificationOptions} .tracking`); + click(`${notificationOptions} .dropdown-menu .watching`); + + andThen(() => { + ok( + exists(`${notificationOptions} .watching`), + "it should display the right notification level" + ); + + // TODO: tgxworld I can't figure out why the topic timeline doesn't show when + // running the tests in phantomjs + // ok( + // exists(".timeline-footer-controls .notification-options .watching"), + // 'it should display the right notification level in topic timeline' + // ); + }); +}); diff --git a/test/javascripts/components/combo-box-test.js.es6 b/test/javascripts/components/combo-box-test.js.es6 index 174905548e..39232362ff 100644 --- a/test/javascripts/components/combo-box-test.js.es6 +++ b/test/javascripts/components/combo-box-test.js.es6 @@ -56,3 +56,19 @@ componentTest('with none', { assert.equal(this.$("select option:eq(2)").text(), 'trout'); } }); + +componentTest('with Object none', { + template: '{{combo-box content=items none=none value=value selected="something"}}', + setup() { + this.set('none', { id: 'something', name: 'none' }); + this.set('items', ['evil', 'trout', 'hat']); + }, + + test(assert) { + assert.equal(this.get('value'), 'something'); + assert.equal(this.$("select option:eq(0)").text(), 'none'); + assert.equal(this.$("select option:eq(0)").val(), 'something'); + assert.equal(this.$("select option:eq(1)").text(), 'evil'); + assert.equal(this.$("select option:eq(2)").text(), 'trout'); + } +}); diff --git a/test/javascripts/models/composer-test.js.es6 b/test/javascripts/models/composer-test.js.es6 index d7bcf53af7..039d3a18d5 100644 --- a/test/javascripts/models/composer-test.js.es6 +++ b/test/javascripts/models/composer-test.js.es6 @@ -160,6 +160,14 @@ test("Title length for private messages", function() { ok(composer.get('titleLengthValid'), "in the range is okay"); }); +test("Post length for private messages with non human users", function() { + const composer = createComposer({ + topic: Ember.Object.create({ pm_with_non_human_user: true }) + }); + + equal(composer.get('minimumPostLength'), 1); +}); + test('editingFirstPost', function() { const composer = createComposer(); ok(!composer.get('editingFirstPost'), "it's false by default"); diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index 1834bfe8a7..02f9f58082 100644 --- a/test/javascripts/test_helper.js +++ b/test/javascripts/test_helper.js @@ -39,7 +39,7 @@ //= require plugin_tests //= require_self // -//= require jquery.magnific-popup-min.js +//= require jquery.magnific-popup.min.js window.inTestEnv = true; diff --git a/test/stylesheets/test_helper.css b/test/stylesheets/test_helper.css index f0e5ac700c..f9eddae24d 100644 --- a/test/stylesheets/test_helper.css +++ b/test/stylesheets/test_helper.css @@ -1,7 +1,5 @@ -/* - *= require desktop - *= require_tree . -*/ +@import '/stylesheets/desktop.css'; + .modal-backdrop { display: none; }